Adding a reconnect feature in case first Pusher request fails

Now, if the first pusher request fails, a waiting message will be displayed and the application will reconnect when the pusher comes back alive or the network connection is established again.
This commit is contained in:
David Négrier 2021-11-29 18:15:21 +01:00
parent 9eb4206fe0
commit fcf0888864
17 changed files with 981 additions and 32 deletions

View File

@ -54,6 +54,7 @@
"queue-typescript": "^1.0.1", "queue-typescript": "^1.0.1",
"quill": "1.3.6", "quill": "1.3.6",
"quill-delta-to-html": "^0.12.0", "quill-delta-to-html": "^0.12.0",
"retry-axios": "^2.6.0",
"rxjs": "^6.6.3", "rxjs": "^6.6.3",
"simple-peer": "^9.11.0", "simple-peer": "^9.11.0",
"socket.io-client": "^2.3.0", "socket.io-client": "^2.3.0",

View File

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import {errorStore} from "../../Stores/ErrorStore"; import {errorStore, hasClosableMessagesInErrorStore} from "../../Stores/ErrorStore";
function close(): boolean { function close(): boolean {
errorStore.clearMessages(); errorStore.clearClosableMessages();
return false; return false;
} }
@ -12,12 +12,14 @@ function close(): boolean {
<p class="nes-text is-error title">Error</p> <p class="nes-text is-error title">Error</p>
<div class="body"> <div class="body">
{#each $errorStore as error} {#each $errorStore as error}
<p>{error}</p> <p>{error.message}</p>
{/each} {/each}
</div> </div>
{#if $hasClosableMessagesInErrorStore}
<div class="button-bar"> <div class="button-bar">
<button class="nes-btn is-error" on:click={close}>Close</button> <button class="nes-btn is-error" on:click={close}>Close</button>
</div> </div>
{/if}
</div> </div>
<style lang="scss"> <style lang="scss">

View File

@ -0,0 +1,37 @@
import axios from "axios";
import * as rax from "retry-axios";
import {errorStore} from "../Stores/ErrorStore";
/**
* This instance of Axios will retry in case of an issue and display an error message as a HTML overlay.
*/
export const axiosWithRetry = axios.create();
axiosWithRetry.defaults.raxConfig = {
instance: axiosWithRetry,
retry: Infinity,
noResponseRetries: Infinity,
maxRetryAfter: 60_000,
// You can detect when a retry is happening, and figure out how many
// retry attempts have been made
onRetryAttempt: err => {
const cfg = rax.getConfig(err);
console.log(err)
console.log(cfg)
console.log(`Retry attempt #${cfg?.currentRetryAttempt}`);
errorStore.addErrorMessage('Unable to connect to WorkAdventure. Are you connected to internet?', {
closable: false,
id: "axios_retry"
});
},
};
axiosWithRetry.interceptors.response.use(res => {
if (res.status < 400) {
errorStore.clearMessageById("axios_retry");
}
return res;
});
const interceptorId = rax.attach(axiosWithRetry);

View File

@ -1,7 +1,10 @@
import * as rax from 'retry-axios';
import Axios from "axios"; import Axios from "axios";
import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable";
import type { CharacterTexture } from "./LocalUser"; import type { CharacterTexture } from "./LocalUser";
import { localUserStore } from "./LocalUserStore"; import { localUserStore } from "./LocalUserStore";
import axios from "axios";
import {axiosWithRetry} from "./AxiosUtils";
export class MapDetail { export class MapDetail {
constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {}
@ -90,7 +93,7 @@ export class Room {
private async getMapDetail(): Promise<MapDetail | RoomRedirect> { private async getMapDetail(): Promise<MapDetail | RoomRedirect> {
try { try {
const result = await Axios.get(`${PUSHER_URL}/map`, { const result = await axiosWithRetry.get(`${PUSHER_URL}/map`, {
params: { params: {
playUri: this.roomUrl.toString(), playUri: this.roomUrl.toString(),
authToken: localUserStore.getAuthToken(), authToken: localUserStore.getAuthToken(),

View File

@ -1,15 +1,24 @@
import { writable } from "svelte/store"; import {derived, writable} from "svelte/store";
interface ErrorMessage {
id: string|undefined;
closable: boolean; // Whether it can be closed by a user action or not
message: string | number | boolean | undefined;
}
/** /**
* A store that contains a list of error messages to be displayed. * A store that contains a list of error messages to be displayed.
*/ */
function createErrorStore() { function createErrorStore() {
const { subscribe, set, update } = writable<string[]>([]); const { subscribe, set, update } = writable<ErrorMessage[]>([]);
return { return {
subscribe, subscribe,
addErrorMessage: (e: string | Error): void => { addErrorMessage: (e: string | Error, options?: {
update((messages: string[]) => { closable?: boolean,
id?: string
}): void => {
update((messages: ErrorMessage[]) => {
let message: string; let message: string;
if (e instanceof Error) { if (e instanceof Error) {
message = e.message; message = e.message;
@ -17,17 +26,36 @@ function createErrorStore() {
message = e; message = e;
} }
if (!messages.includes(message)) { if (!messages.find(errorMessage => errorMessage.message === message)) {
messages.push(message); messages.push({
message,
closable: options?.closable ?? true,
id: options?.id
});
} }
return messages; return messages;
}); });
}, },
clearMessages: (): void => { clearMessageById: (id: string): void => {
set([]); update((messages: ErrorMessage[]) => {
messages = messages.filter(message => message.id !== id);
return messages;
});
},
clearClosableMessages: (): void => {
update((messages: ErrorMessage[]) => {
messages = messages.filter(message => message.closable);
return messages;
});
}, },
}; };
} }
export const errorStore = createErrorStore(); export const errorStore = createErrorStore();
export const hasClosableMessagesInErrorStore = derived(errorStore, ($errorStore) => {
const closableMessage = $errorStore.find(errorMessage => errorMessage.closable);
return !!closableMessage;
});

View File

@ -4862,6 +4862,11 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
retry-axios@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/retry-axios/-/retry-axios-2.6.0.tgz#d4dc5c8a8e73982e26a705e46a33df99a28723e0"
integrity sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==
retry@^0.12.0: retry@^0.12.0:
version "0.12.0" version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"

View File

@ -0,0 +1,33 @@
WA.onInit().then(() => {
console.log('Trying to read variable "doorOpened" whose default property is true. This should display "true".');
console.log('doorOpened', WA.state.loadVariable('doorOpened'));
console.log('Trying to set variable "not_exists". This should display an error in the console, followed by a log saying the error was caught.')
WA.state.saveVariable('not_exists', 'foo').catch((e) => {
console.log('Successfully caught error: ', e);
});
console.log('Trying to set variable "myvar". This should work.');
WA.state.saveVariable('myvar', {'foo': 'bar'});
console.log('Trying to read variable "myvar". This should display a {"foo": "bar"} object.');
console.log(WA.state.loadVariable('myvar'));
console.log('Trying to set variable "myvar" using proxy. This should work.');
WA.state.myvar = {'baz': 42};
console.log('Trying to read variable "myvar" using proxy. This should display a {"baz": 42} object.');
console.log(WA.state.myvar);
console.log('Trying to set variable "config". This should not work because we are not logged as admin.');
WA.state.saveVariable('config', {'foo': 'bar'}).catch(e => {
console.log('Successfully caught error because variable "config" is not writable: ', e);
});
console.log('Trying to read variable "readableByAdmin" that can only be read by "admin". We are not admin so we should not get the default value.');
if (WA.state.readableByAdmin === true) {
console.error('Failed test: readableByAdmin can be read.');
} else {
console.log('Success test: readableByAdmin was not read.');
}
});

View File

@ -0,0 +1,47 @@
<!doctype html>
<html lang="en">
<head>
<script>
var script = document.createElement('script');
// Don't do this at home kids! The "document.referrer" part is actually inserting a XSS security.
// We are OK in this precise case because the HTML page is hosted on the "maps" domain that contains only static files.
document.head.appendChild(script);
window.addEventListener('load', () => {
console.log('On load');
WA.onInit().then(() => {
console.log('After WA init');
const textField = document.getElementById('textField');
textField.value = WA.state.textField;
textField.addEventListener('change', function (evt) {
console.log('saving variable')
WA.state.textField = this.value;
});
WA.state.onVariableChange('textField').subscribe((value) => {
console.log('variable changed received')
textField.value = value;
});
document.getElementById('btn').addEventListener('click', () => {
console.log(WA.state.loadVariable('textField'));
document.getElementById('placeholder').innerText = WA.state.loadVariable('textField');
});
document.getElementById('setUndefined').addEventListener('click', () => {
WA.state.textField = undefined;
document.getElementById('textField').value = '';
});
});
})
</script>
</head>
<body>
<input type="text" id="textField" />
<button id="setUndefined">Delete variable</button>
<button id="btn">Display textField variable value</button>
<div id="placeholder"></div>
</body>
</html>

View File

@ -0,0 +1,131 @@
{ "compressionlevel":-1,
"height":10,
"infinite":false,
"layers":[
{
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"height":10,
"id":1,
"name":"floor",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"shared_variables.html"
},
{
"name":"openWebsiteAllowApi",
"type":"bool",
"value":true
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":2,
"name":"start",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"draworder":"topdown",
"id":3,
"name":"floorLayer",
"objects":[
{
"height":67,
"id":3,
"name":"",
"rotation":0,
"text":
{
"fontfamily":"Sans Serif",
"pixelsize":11,
"text":"Test:\nChange the form\nConnect with another user\n\nResult:\nThe form should open in the same state for the other user\nAlso, a change on one user is directly propagated to the other user",
"wrap":true
},
"type":"",
"visible":true,
"width":252.4375,
"x":2.78125,
"y":2.5
},
{
"height":0,
"id":5,
"name":"textField",
"point":true,
"properties":[
{
"name":"default",
"type":"string",
"value":"default value"
},
{
"name":"jsonSchema",
"type":"string",
"value":"{}"
},
{
"name":"persist",
"type":"bool",
"value":true
},
{
"name":"readableBy",
"type":"string",
"value":""
},
{
"name":"writableBy",
"type":"string",
"value":""
}],
"rotation":0,
"type":"variable",
"visible":true,
"width":0,
"x":57.5,
"y":111
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
}],
"nextlayerid":8,
"nextobjectid":10,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"2021.03.23",
"tileheight":32,
"tilesets":[
{
"columns":11,
"firstgid":1,
"image":"..\/tileset1.png",
"imageheight":352,
"imagewidth":352,
"margin":0,
"name":"tileset1",
"spacing":0,
"tilecount":121,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":1.5,
"width":10
}

View File

@ -26,7 +26,7 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
import { SocketManager, socketManager } from "../Services/SocketManager"; import { SocketManager, socketManager } from "../Services/SocketManager";
import { emitInBatch } from "../Services/IoSocketHelpers"; import { emitInBatch } from "../Services/IoSocketHelpers";
import { ADMIN_SOCKETS_TOKEN, ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
import { Zone } from "_Model/Zone"; import { Zone } from "_Model/Zone";
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
import { CharacterTexture } from "../Services/AdminApi/CharacterTexture"; import { CharacterTexture } from "../Services/AdminApi/CharacterTexture";

View File

@ -1,8 +1,5 @@
import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; import { ADMIN_SOCKETS_TOKEN, SECRET_KEY } from "../Enum/EnvironmentVariable";
import { uuid } from "uuidv4"; import Jwt from "jsonwebtoken";
import Jwt, { verify } from "jsonwebtoken";
import { TokenInterface } from "../Controller/AuthenticateController";
import { adminApi, AdminBannedData } from "../Services/AdminApi";
export interface AuthTokenData { export interface AuthTokenData {
identifier: string; //will be a email if logged in or an uuid if anonymous identifier: string; //will be a email if logged in or an uuid if anonymous

View File

@ -9,5 +9,7 @@ module.exports = {
"path": "screenshots/", "path": "screenshots/",
"takeOnFails": true, "takeOnFails": true,
"thumbnails": false, "thumbnails": false,
} },
"assertionTimeout": 10000,
"selectorTimeout": 20000,
} }

512
tests/package-lock.json generated
View File

@ -4,7 +4,11 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"dependencies": {
"@types/dockerode": "^3.3.0"
},
"devDependencies": { "devDependencies": {
"dockerode": "^3.3.1",
"testcafe": "^1.17.1" "testcafe": "^1.17.1"
} }
}, },
@ -1848,6 +1852,24 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@types/docker-modem": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz",
"integrity": "sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==",
"dependencies": {
"@types/node": "*",
"@types/ssh2": "*"
}
},
"node_modules/@types/dockerode": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz",
"integrity": "sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg==",
"dependencies": {
"@types/docker-modem": "*",
"@types/node": "*"
}
},
"node_modules/@types/error-stack-parser": { "node_modules/@types/error-stack-parser": {
"version": "1.3.18", "version": "1.3.18",
"resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz",
@ -1885,8 +1907,24 @@
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "12.20.37", "version": "12.20.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==", "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA=="
"dev": true },
"node_modules/@types/ssh2": {
"version": "0.5.49",
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.49.tgz",
"integrity": "sha512-ffxhQhJqgTzrw8NxHTgkaDtAmAj2qxCyoves7ztpRgqvzbHcZTpTcm+ATWuuCbPQzxnnF4F3SGGTLGEWTZpwqA==",
"dependencies": {
"@types/node": "*",
"@types/ssh2-streams": "*"
}
},
"node_modules/@types/ssh2-streams": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz",
"integrity": "sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==",
"dependencies": {
"@types/node": "*"
}
}, },
"node_modules/acorn-hammerhead": { "node_modules/acorn-hammerhead": {
"version": "0.5.0", "version": "0.5.0",
@ -2012,6 +2050,15 @@
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true "dev": true
}, },
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assertion-error": { "node_modules/assertion-error": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
@ -2153,12 +2200,46 @@
} }
] ]
}, },
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/bin-v8-flags-filter": { "node_modules/bin-v8-flags-filter": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz", "resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz",
"integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==", "integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==",
"dev": true "dev": true
}, },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/bl/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/bluebird": { "node_modules/bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@ -2225,6 +2306,30 @@
"url": "https://opencollective.com/browserslist" "url": "https://opencollective.com/browserslist"
} }
}, },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -2319,6 +2424,12 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"node_modules/chrome-remote-interface": { "node_modules/chrome-remote-interface": {
"version": "0.30.1", "version": "0.30.1",
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz", "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz",
@ -2449,6 +2560,20 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true "dev": true
}, },
"node_modules/cpu-features": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz",
"integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"dependencies": {
"nan": "^2.14.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -2649,6 +2774,60 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/docker-modem": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz",
"integrity": "sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==",
"dev": true,
"dependencies": {
"debug": "^4.1.1",
"readable-stream": "^3.5.0",
"split-ca": "^1.0.1",
"ssh2": "^1.4.0"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/docker-modem/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/dockerode": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz",
"integrity": "sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==",
"dev": true,
"dependencies": {
"docker-modem": "^3.0.0",
"tar-fs": "~2.0.1"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/dockerode/node_modules/tar-fs": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
"dev": true,
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.0.0"
}
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.3.906", "version": "1.3.906",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz",
@ -2852,6 +3031,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3100,6 +3285,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.1.9", "version": "5.1.9",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz",
@ -3549,6 +3754,12 @@
"mkdirp": "bin/cmd.js" "mkdirp": "bin/cmd.js"
} }
}, },
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"dev": true
},
"node_modules/moment": { "node_modules/moment": {
"version": "2.29.1", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
@ -3582,6 +3793,13 @@
"npm": ">=1.4.0" "npm": ">=1.4.0"
} }
}, },
"node_modules/nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
"dev": true,
"optional": true
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "1.3.4", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz",
@ -4281,6 +4499,30 @@
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
"dev": true "dev": true
}, },
"node_modules/split-ca": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=",
"dev": true
},
"node_modules/ssh2": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.5.0.tgz",
"integrity": "sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"asn1": "^0.2.4",
"bcrypt-pbkdf": "^1.0.2"
},
"engines": {
"node": ">=10.16.0"
},
"optionalDependencies": {
"cpu-features": "0.0.2",
"nan": "^2.15.0"
}
},
"node_modules/stackframe": { "node_modules/stackframe": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz",
@ -4355,6 +4597,36 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tar-stream/node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/testcafe": { "node_modules/testcafe": {
"version": "1.17.1", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz",
@ -4889,6 +5161,12 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
"node_modules/type-detect": { "node_modules/type-detect": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@ -6362,6 +6640,24 @@
"fastq": "^1.6.0" "fastq": "^1.6.0"
} }
}, },
"@types/docker-modem": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.2.tgz",
"integrity": "sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==",
"requires": {
"@types/node": "*",
"@types/ssh2": "*"
}
},
"@types/dockerode": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.0.tgz",
"integrity": "sha512-3Mc0b2gnypJB8Gwmr+8UVPkwjpf4kg1gVxw8lAI4Y/EzpK50LixU1wBSPN9D+xqiw2Ubb02JO8oM0xpwzvi2mg==",
"requires": {
"@types/docker-modem": "*",
"@types/node": "*"
}
},
"@types/error-stack-parser": { "@types/error-stack-parser": {
"version": "1.3.18", "version": "1.3.18",
"resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz",
@ -6399,8 +6695,24 @@
"@types/node": { "@types/node": {
"version": "12.20.37", "version": "12.20.37",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz",
"integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==", "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA=="
"dev": true },
"@types/ssh2": {
"version": "0.5.49",
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.49.tgz",
"integrity": "sha512-ffxhQhJqgTzrw8NxHTgkaDtAmAj2qxCyoves7ztpRgqvzbHcZTpTcm+ATWuuCbPQzxnnF4F3SGGTLGEWTZpwqA==",
"requires": {
"@types/node": "*",
"@types/ssh2-streams": "*"
}
},
"@types/ssh2-streams": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.9.tgz",
"integrity": "sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==",
"requires": {
"@types/node": "*"
}
}, },
"acorn-hammerhead": { "acorn-hammerhead": {
"version": "0.5.0", "version": "0.5.0",
@ -6498,6 +6810,15 @@
} }
} }
}, },
"asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"dev": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assertion-error": { "assertion-error": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
@ -6600,12 +6921,45 @@
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true "dev": true
}, },
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"dev": true,
"requires": {
"tweetnacl": "^0.14.3"
}
},
"bin-v8-flags-filter": { "bin-v8-flags-filter": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz", "resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz",
"integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==", "integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==",
"dev": true "dev": true
}, },
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"bluebird": { "bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@ -6659,6 +7013,16 @@
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
} }
}, },
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"dev": true,
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"buffer-from": { "buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@ -6734,6 +7098,12 @@
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
"dev": true "dev": true
}, },
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"dev": true
},
"chrome-remote-interface": { "chrome-remote-interface": {
"version": "0.30.1", "version": "0.30.1",
"resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz", "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz",
@ -6842,6 +7212,16 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true "dev": true
}, },
"cpu-features": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz",
"integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==",
"dev": true,
"optional": true,
"requires": {
"nan": "^2.14.1"
}
},
"cross-spawn": { "cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -7000,6 +7380,55 @@
"path-type": "^4.0.0" "path-type": "^4.0.0"
} }
}, },
"docker-modem": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz",
"integrity": "sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"readable-stream": "^3.5.0",
"split-ca": "^1.0.1",
"ssh2": "^1.4.0"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"dockerode": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz",
"integrity": "sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==",
"dev": true,
"requires": {
"docker-modem": "^3.0.0",
"tar-fs": "~2.0.1"
},
"dependencies": {
"tar-fs": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.0.0"
}
}
}
},
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.906", "version": "1.3.906",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz",
@ -7165,6 +7594,12 @@
"locate-path": "^3.0.0" "locate-path": "^3.0.0"
} }
}, },
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true
},
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -7357,6 +7792,12 @@
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
}, },
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true
},
"ignore": { "ignore": {
"version": "5.1.9", "version": "5.1.9",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz",
@ -7699,6 +8140,12 @@
"minimist": "^1.2.5" "minimist": "^1.2.5"
} }
}, },
"mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"dev": true
},
"moment": { "moment": {
"version": "2.29.1", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
@ -7723,6 +8170,13 @@
"integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==",
"dev": true "dev": true
}, },
"nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
"dev": true,
"optional": true
},
"nanoid": { "nanoid": {
"version": "1.3.4", "version": "1.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz",
@ -8257,6 +8711,24 @@
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
"dev": true "dev": true
}, },
"split-ca": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY=",
"dev": true
},
"ssh2": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.5.0.tgz",
"integrity": "sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA==",
"dev": true,
"requires": {
"asn1": "^0.2.4",
"bcrypt-pbkdf": "^1.0.2",
"cpu-features": "0.0.2",
"nan": "^2.15.0"
}
},
"stackframe": { "stackframe": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz",
@ -8316,6 +8788,32 @@
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
}, },
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dev": true,
"requires": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"dependencies": {
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"testcafe": { "testcafe": {
"version": "1.17.1", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz",
@ -8772,6 +9270,12 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"dev": true
},
"type-detect": { "type-detect": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",

View File

@ -1,8 +1,12 @@
{ {
"devDependencies": { "devDependencies": {
"dockerode": "^3.3.1",
"testcafe": "^1.17.1" "testcafe": "^1.17.1"
}, },
"scripts": { "scripts": {
"test": "testcafe" "test": "testcafe"
},
"dependencies": {
"@types/dockerode": "^3.3.0"
} }
} }

View File

@ -1,13 +1,38 @@
import {assertLogMessage} from "./utils/log"; import {assertLogMessage} from "./utils/log";
const fs = require('fs') const fs = require('fs');
const Docker = require('dockerode');
import { Selector } from 'testcafe'; import { Selector } from 'testcafe';
import {userAlice} from "./utils/roles"; import {userAlice} from "./utils/roles";
import {findContainer, rebootBack, rebootPusher, rebootRedis, startContainer, stopContainer} from "./utils/containers";
// Note: we are also testing that we can connect if the URL contains a random query string fixture `Reconnection`
fixture `Variables` .page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json`;
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json?somerandomparam=1`;
test("Test that connection can succeed even if WorkAdventure starts while pusher is down", async (t: TestController) => {
// Let's stop the pusher
const container = await findContainer('pusher');
await stopContainer(container);
const errorMessage = Selector('.error-div');
await t
.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json')
.expect(errorMessage.innerText).contains('Unable to connect to WorkAdventure')
await startContainer(container);
await t.useRole(userAlice);
t.ctx.passed = true;
}).after(async t => {
if (!t.ctx.passed) {
console.log("Test failed. Browser logs:")
console.log(await t.getBrowserConsoleMessages());
}
});
/*
test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async (t: TestController) => { test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async (t: TestController) => {
// Let's start by visiting a map that DOES not have the variable. // Let's start by visiting a map that DOES not have the variable.
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json'); fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json');
@ -18,11 +43,9 @@ test("Test that variables cache in the back don't prevent setting a variable in
// Let's REPLACE the map by a map that has a new variable // Let's REPLACE the map by a map that has a new variable
// At this point, the back server contains a cache of the old map (with no variables) // At this point, the back server contains a cache of the old map (with no variables)
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_2.json', '../maps/tests/Variables/Cache/variables_tmp.json'); fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_2.json', '../maps/tests/Variables/Cache/variables_tmp.json');
await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json'); await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json')
.resizeWindow(960, 800)
await t.resizeWindow(960, 800); .useRole(userAlice);
await t.useRole(userAlice);
//.takeScreenshot('after_switch.png'); //.takeScreenshot('after_switch.png');
// Let's check we successfully manage to save the variable value. // Let's check we successfully manage to save the variable value.
@ -35,3 +58,4 @@ test("Test that variables cache in the back don't prevent setting a variable in
console.log(await t.getBrowserConsoleMessages()); console.log(await t.getBrowserConsoleMessages());
} }
}); });
*/

View File

@ -0,0 +1,51 @@
//import Docker from "dockerode";
//import * as Dockerode from "dockerode";
import Dockerode = require( 'dockerode')
/**
* Returns a container ID based on the container name.
*/
export async function findContainer(name: string): Promise<Dockerode.ContainerInfo> {
const docker = new Dockerode();
const containers = await docker.listContainers();
const foundContainer = containers.find((container) => container.State === 'running' && container.Names.find((containerName) => containerName.includes(name)));
if (foundContainer === undefined) {
throw new Error('Could not find a running container whose name contains "'+name+'"');
}
return foundContainer;
}
export async function stopContainer(container: Dockerode.ContainerInfo): Promise<void> {
const docker = new Dockerode();
await docker.getContainer(container.Id).stop();
}
export async function startContainer(container: Dockerode.ContainerInfo): Promise<void> {
const docker = new Dockerode();
await docker.getContainer(container.Id).start();
}
export async function rebootBack(): Promise<void> {
const container = await findContainer('back');
await stopContainer(container);
await startContainer(container);
}
export async function rebootPusher(): Promise<void> {
const container = await findContainer('pusher');
await stopContainer(container);
await startContainer(container);
}
export async function rebootRedis(): Promise<void> {
const container = await findContainer('redis');
await stopContainer(container);
await startContainer(container);
}

80
tests/tests/variables.ts Normal file
View File

@ -0,0 +1,80 @@
import {assertLogMessage} from "./utils/log";
const fs = require('fs');
const Docker = require('dockerode');
import { Selector } from 'testcafe';
import {userAlice} from "./utils/roles";
import {findContainer, rebootBack, rebootPusher, rebootRedis, startContainer, stopContainer} from "./utils/containers";
// Note: we are also testing that passing a random query parameter does not cause any issue.
fixture `Variables`
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1`;
test("Test that variables storage works", async (t: TestController) => {
const variableInput = Selector('#textField');
await Promise.all([
rebootBack(),
rebootRedis(),
rebootPusher(),
]);
await t.useRole(userAlice)
.switchToIframe("#cowebsite-buffer iframe")
.debug()
.expect(variableInput.value).eql('default value')
.typeText(variableInput, 'new value')
.switchToPreviousWindow()
// reload
/*.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json')
.switchToIframe("#cowebsite-buffer iframe")
.expect(variableInput.value).eql('new value')*/
/*
const backContainer = await findContainer('back');
await stopContainer(backContainer);
await startContainer(backContainer);
*/
t.ctx.passed = true;
}).after(async t => {
if (!t.ctx.passed) {
console.log("Test failed. Browser logs:")
console.log(await t.getBrowserConsoleMessages());
}
});
test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async (t: TestController) => {
// Let's start by visiting a map that DOES not have the variable.
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json');
await t.useRole(userAlice);
//.takeScreenshot('before_switch.png');
// Let's REPLACE the map by a map that has a new variable
// At this point, the back server contains a cache of the old map (with no variables)
fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_2.json', '../maps/tests/Variables/Cache/variables_tmp.json');
await t.openWindow('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json')
.resizeWindow(960, 800)
.useRole(userAlice);
//.takeScreenshot('after_switch.png');
// Let's check we successfully manage to save the variable value.
await assertLogMessage(t, 'SUCCESS!');
t.ctx.passed = true;
}).after(async t => {
if (!t.ctx.passed) {
console.log("Test failed. Browser logs:")
console.log(await t.getBrowserConsoleMessages());
}
});