From 0ecabd14f1770a90945488853b150d175f4b53aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 20 Sep 2021 09:14:14 +0200 Subject: [PATCH 01/42] Switching API to use type="module" scripts This allows using imports inside scripts imported by WorkAdventure out of the box (and therefore easily importing the scripting-api-extra without resorting to using a bundler) --- front/dist/iframe.html | 1 + front/src/Api/IframeListener.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/front/dist/iframe.html b/front/dist/iframe.html index c8fafb4b..02dc0fd8 100644 --- a/front/dist/iframe.html +++ b/front/dist/iframe.html @@ -11,6 +11,7 @@ const scriptUrl = urlParams.get('script'); const script = document.createElement('script'); script.src = scriptUrl; + script.type = "module"; document.head.append(script); diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 5a9aca85..64d0e8f2 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -326,7 +326,7 @@ class IframeListener { "//" + window.location.host + '/iframe_api.js" >\n' + - '\n' + "\n" + From fcf08888649ff8f73626a6dda4e5f56bbf3028ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 29 Nov 2021 18:15:21 +0100 Subject: [PATCH 02/42] 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. --- front/package.json | 1 + front/src/Components/UI/ErrorDialog.svelte | 8 +- front/src/Connexion/AxiosUtils.ts | 37 ++ front/src/Connexion/Room.ts | 5 +- front/src/Stores/ErrorStore.ts | 44 +- front/yarn.lock | 5 + maps/tests/Variables/E2ETests/script.js | 33 ++ .../Variables/E2ETests/shared_variables.html | 47 ++ .../Variables/E2ETests/shared_variables.json | 131 +++++ pusher/src/Controller/IoSocketController.ts | 2 +- pusher/src/Services/JWTTokenManager.ts | 7 +- tests/.testcaferc.js | 4 +- tests/package-lock.json | 512 +++++++++++++++++- tests/package.json | 4 + tests/tests/{test.ts => reconnect.ts} | 42 +- tests/tests/utils/containers.ts | 51 ++ tests/tests/variables.ts | 80 +++ 17 files changed, 981 insertions(+), 32 deletions(-) create mode 100644 front/src/Connexion/AxiosUtils.ts create mode 100644 maps/tests/Variables/E2ETests/script.js create mode 100644 maps/tests/Variables/E2ETests/shared_variables.html create mode 100644 maps/tests/Variables/E2ETests/shared_variables.json rename tests/tests/{test.ts => reconnect.ts} (56%) create mode 100644 tests/tests/utils/containers.ts create mode 100644 tests/tests/variables.ts diff --git a/front/package.json b/front/package.json index ec81d8a7..6a9db0ba 100644 --- a/front/package.json +++ b/front/package.json @@ -54,6 +54,7 @@ "queue-typescript": "^1.0.1", "quill": "1.3.6", "quill-delta-to-html": "^0.12.0", + "retry-axios": "^2.6.0", "rxjs": "^6.6.3", "simple-peer": "^9.11.0", "socket.io-client": "^2.3.0", diff --git a/front/src/Components/UI/ErrorDialog.svelte b/front/src/Components/UI/ErrorDialog.svelte index 3244de24..bf203819 100644 --- a/front/src/Components/UI/ErrorDialog.svelte +++ b/front/src/Components/UI/ErrorDialog.svelte @@ -1,8 +1,8 @@ + + + + + + + +
+ + diff --git a/maps/tests/Variables/E2ETests/shared_variables.json b/maps/tests/Variables/E2ETests/shared_variables.json new file mode 100644 index 00000000..2de5e4c0 --- /dev/null +++ b/maps/tests/Variables/E2ETests/shared_variables.json @@ -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 +} \ No newline at end of file diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 35fd08d5..b84d320a 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -26,7 +26,7 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { SocketManager, socketManager } from "../Services/SocketManager"; 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 { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { CharacterTexture } from "../Services/AdminApi/CharacterTexture"; diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 40b5b824..3d580c1e 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -1,8 +1,5 @@ -import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; -import { uuid } from "uuidv4"; -import Jwt, { verify } from "jsonwebtoken"; -import { TokenInterface } from "../Controller/AuthenticateController"; -import { adminApi, AdminBannedData } from "../Services/AdminApi"; +import { ADMIN_SOCKETS_TOKEN, SECRET_KEY } from "../Enum/EnvironmentVariable"; +import Jwt from "jsonwebtoken"; export interface AuthTokenData { identifier: string; //will be a email if logged in or an uuid if anonymous diff --git a/tests/.testcaferc.js b/tests/.testcaferc.js index 8d2ce364..e89af5b3 100644 --- a/tests/.testcaferc.js +++ b/tests/.testcaferc.js @@ -9,5 +9,7 @@ module.exports = { "path": "screenshots/", "takeOnFails": true, "thumbnails": false, - } + }, + "assertionTimeout": 10000, + "selectorTimeout": 20000, } diff --git a/tests/package-lock.json b/tests/package-lock.json index 9fb72bba..fc3cf124 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -4,7 +4,11 @@ "requires": true, "packages": { "": { + "dependencies": { + "@types/dockerode": "^3.3.0" + }, "devDependencies": { + "dockerode": "^3.3.1", "testcafe": "^1.17.1" } }, @@ -1848,6 +1852,24 @@ "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": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", @@ -1885,8 +1907,24 @@ "node_modules/@types/node": { "version": "12.20.37", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz", - "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==", - "dev": true + "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==" + }, + "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": { "version": "0.5.0", @@ -2012,6 +2050,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "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": { "version": "1.1.0", "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz", "integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==", "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": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -2225,6 +2306,30 @@ "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": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2319,6 +2424,12 @@ "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": { "version": "0.30.1", "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz", @@ -2449,6 +2560,20 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "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": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2649,6 +2774,60 @@ "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": { "version": "1.3.906", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz", @@ -2852,6 +3031,12 @@ "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": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3100,6 +3285,26 @@ "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": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", @@ -3549,6 +3754,12 @@ "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": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -3582,6 +3793,13 @@ "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": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", @@ -4281,6 +4499,30 @@ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "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": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", @@ -4355,6 +4597,36 @@ "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": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", @@ -4889,6 +5161,12 @@ "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": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -6362,6 +6640,24 @@ "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": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz", @@ -6399,8 +6695,24 @@ "@types/node": { "version": "12.20.37", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.37.tgz", - "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==", - "dev": true + "integrity": "sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA==" + }, + "@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": { "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": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -6600,12 +6921,45 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "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": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bin-v8-flags-filter/-/bin-v8-flags-filter-1.2.0.tgz", "integrity": "sha512-g8aeYkY7GhyyKRvQMBsJQZjhm2iCX3dKYvfrMpwVR8IxmUGrkpCBFoKbB9Rh0o3sTLCjU/1tFpZ4C7j3f+D+3g==", "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": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -6659,6 +7013,16 @@ "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": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6734,6 +7098,12 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "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": { "version": "0.30.1", "resolved": "https://registry.npmjs.org/chrome-remote-interface/-/chrome-remote-interface-0.30.1.tgz", @@ -6842,6 +7212,16 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "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": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7000,6 +7380,55 @@ "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": { "version": "1.3.906", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.906.tgz", @@ -7165,6 +7594,12 @@ "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": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7357,6 +7792,12 @@ "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": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", @@ -7699,6 +8140,12 @@ "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": { "version": "2.29.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", @@ -7723,6 +8170,13 @@ "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==", "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": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.3.4.tgz", @@ -8257,6 +8711,24 @@ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "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": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", @@ -8316,6 +8788,32 @@ "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": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", @@ -8772,6 +9270,12 @@ "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": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/tests/package.json b/tests/package.json index 848bc7f6..f36c5ea4 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,8 +1,12 @@ { "devDependencies": { + "dockerode": "^3.3.1", "testcafe": "^1.17.1" }, "scripts": { "test": "testcafe" + }, + "dependencies": { + "@types/dockerode": "^3.3.0" } } diff --git a/tests/tests/test.ts b/tests/tests/reconnect.ts similarity index 56% rename from tests/tests/test.ts rename to tests/tests/reconnect.ts index 94659ad7..b4a052c5 100644 --- a/tests/tests/test.ts +++ b/tests/tests/reconnect.ts @@ -1,13 +1,38 @@ import {assertLogMessage} from "./utils/log"; -const fs = require('fs') +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 we can connect if the URL contains a random query string -fixture `Variables` - .page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json?somerandomparam=1`; +fixture `Reconnection` + .page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json`; +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) => { // 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'); @@ -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 // 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'); - - await t.resizeWindow(960, 800); - - await t.useRole(userAlice); + 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. @@ -35,3 +58,4 @@ test("Test that variables cache in the back don't prevent setting a variable in console.log(await t.getBrowserConsoleMessages()); } }); +*/ diff --git a/tests/tests/utils/containers.ts b/tests/tests/utils/containers.ts new file mode 100644 index 00000000..6767b6dc --- /dev/null +++ b/tests/tests/utils/containers.ts @@ -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 { + 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 { + const docker = new Dockerode(); + + await docker.getContainer(container.Id).stop(); +} + +export async function startContainer(container: Dockerode.ContainerInfo): Promise { + const docker = new Dockerode(); + + await docker.getContainer(container.Id).start(); +} + +export async function rebootBack(): Promise { + const container = await findContainer('back'); + await stopContainer(container); + await startContainer(container); +} + +export async function rebootPusher(): Promise { + const container = await findContainer('pusher'); + await stopContainer(container); + await startContainer(container); +} + +export async function rebootRedis(): Promise { + const container = await findContainer('redis'); + await stopContainer(container); + await startContainer(container); +} diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts new file mode 100644 index 00000000..905a4313 --- /dev/null +++ b/tests/tests/variables.ts @@ -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()); + } +}); + From a3b20cf0659ce725524a3b73fd6d88f81a4071a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 1 Dec 2021 14:29:51 +0100 Subject: [PATCH 03/42] Installing NPM dependencies in CI for tests --- .github/workflows/end_to_end_tests.yml | 9 +++++++++ tests/.testcaferc.js | 2 +- tests/tests/variables.ts | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index a7b3ecfb..d175d8ab 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -20,6 +20,15 @@ jobs: - name: "Checkout" uses: "actions/checkout@v2.0.0" + - name: "Setup NodeJS" + uses: actions/setup-node@v1 + with: + node-version: '14.x' + + - name: "Install dependencies" + run: yarn install + working-directory: "tests" + - name: "Setup .env file" run: cp .env.template .env diff --git a/tests/.testcaferc.js b/tests/.testcaferc.js index e89af5b3..9f7628c5 100644 --- a/tests/.testcaferc.js +++ b/tests/.testcaferc.js @@ -11,5 +11,5 @@ module.exports = { "thumbnails": false, }, "assertionTimeout": 10000, - "selectorTimeout": 20000, + "selectorTimeout": 40000, } diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index 905a4313..a3d16fda 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -22,7 +22,6 @@ test("Test that variables storage works", async (t: TestController) => { await t.useRole(userAlice) .switchToIframe("#cowebsite-buffer iframe") - .debug() .expect(variableInput.value).eql('default value') .typeText(variableInput, 'new value') .switchToPreviousWindow() From 7326e2a38c5255be0a96e155f4307ffb438d80d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 1 Dec 2021 14:37:36 +0100 Subject: [PATCH 04/42] Mounting Docker socket in testcafe container --- .github/workflows/end_to_end_tests.yml | 2 +- docker-compose.testcafe.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index d175d8ab..0e7e61b7 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -26,7 +26,7 @@ jobs: node-version: '14.x' - name: "Install dependencies" - run: yarn install + run: npm install working-directory: "tests" - name: "Setup .env file" diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index 774477a1..bbd5e5ff 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -8,5 +8,6 @@ services: volumes: - ./tests:/tests - ./maps:/maps + - /var/run/docker.sock:/var/run/docker.sock # security_opt: # - seccomp:unconfined From 6be57a2b3c9ff5a1cbd73c27592c9deee65c4715 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Dec 2021 18:29:39 +0000 Subject: [PATCH 05/42] Bump @joeattardi/emoji-button from 4.6.0 to 4.6.2 in /front Bumps [@joeattardi/emoji-button](https://github.com/joeattardi/emoji-button) from 4.6.0 to 4.6.2. - [Release notes](https://github.com/joeattardi/emoji-button/releases) - [Commits](https://github.com/joeattardi/emoji-button/compare/v4.6.0...v4.6.2) --- updated-dependencies: - dependency-name: "@joeattardi/emoji-button" dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- front/package.json | 2 +- front/yarn.lock | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/front/package.json b/front/package.json index ec81d8a7..52c4b59c 100644 --- a/front/package.json +++ b/front/package.json @@ -40,7 +40,7 @@ }, "dependencies": { "@fontsource/press-start-2p": "^4.3.0", - "@joeattardi/emoji-button": "^4.6.0", + "@joeattardi/emoji-button": "^4.6.2", "@types/simple-peer": "^9.11.1", "@types/socket.io-client": "^1.4.32", "axios": "^0.21.2", diff --git a/front/yarn.lock b/front/yarn.lock index f94e659f..a9e73252 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -88,16 +88,17 @@ dependencies: "@fortawesome/fontawesome-common-types" "^0.2.35" -"@joeattardi/emoji-button@^4.6.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@joeattardi/emoji-button/-/emoji-button-4.6.0.tgz#93a78abb19c61ce9dcc464d15f373743b15cd2a2" - integrity sha512-KwOE1j+YxX47JmT0pXNCa+9Ai4Wf2fmABtvuxy6JBJ5QV0HdoThRKjL6CxAreVwwLbNQ/PDoR36xpc5QJjLXPA== +"@joeattardi/emoji-button@^4.6.2": + version "4.6.2" + resolved "https://registry.yarnpkg.com/@joeattardi/emoji-button/-/emoji-button-4.6.2.tgz#75baf4ce27324e4d6fb90292f8b248235f638ad0" + integrity sha512-FhuzTmW3nVHLVp2BJfNX17CYV77fqtKZlx328D4h6Dw3cPTT1gJRNXN0jV7BvHgsl6Q/tN8DIQQxTUIO4jW3gQ== dependencies: "@fortawesome/fontawesome-svg-core" "^1.2.28" "@fortawesome/free-regular-svg-icons" "^5.13.0" "@fortawesome/free-solid-svg-icons" "^5.13.0" "@popperjs/core" "^2.4.0" "@types/twemoji" "^12.1.1" + escape-html "^1.0.3" focus-trap "^5.1.0" fuzzysort "^1.1.4" tiny-emitter "^2.1.0" @@ -2030,7 +2031,7 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -5756,16 +5757,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tslib@^2.0.3, tslib@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" - integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== - tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" From bf830a67cb33d4eb3eb6d8a91fa77011429716c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 09:23:31 +0100 Subject: [PATCH 06/42] Fixing debug controller in back --- back/src/Controller/DebugController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts index 88287753..5210667b 100644 --- a/back/src/Controller/DebugController.ts +++ b/back/src/Controller/DebugController.ts @@ -11,7 +11,7 @@ export class DebugController { } getDump() { - this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => { + this.App.get("/dump", async (res: HttpResponse, req: HttpRequest) => { const query = parse(req.getQuery()); if (query.token !== ADMIN_API_TOKEN) { @@ -22,7 +22,7 @@ export class DebugController { .writeStatus("200 OK") .writeHeader("Content-Type", "application/json") .end( - stringify(socketManager.getWorlds(), (key: unknown, value: unknown) => { + stringify(await Promise.all(socketManager.getWorlds().values()), (key: unknown, value: unknown) => { if (key === "listeners") { return "Listeners"; } From 9bba6069b4c62b2e2f760add69d185235ddf98b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 09:23:52 +0100 Subject: [PATCH 07/42] Making LIVE_RELOAD configurable --- docker-compose.yaml | 17 +++++++++++++---- front/webpack.config.ts | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index e5e6489c..f7563fb9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,20 +1,21 @@ version: "3" services: reverse-proxy: - image: traefik:v2.0 + image: traefik:v2.5 command: - --api.insecure=true - --providers.docker - --entryPoints.web.address=:80 - --entryPoints.websecure.address=:443 + - "--providers.docker.exposedbydefault=false" ports: - "80:80" - "443:443" # The Web UI (enabled by --api.insecure=true) - "8080:8080" - depends_on: - - back - - front + #depends_on: + # - back + # - front volumes: - /var/run/docker.sock:/var/run/docker.sock networks: @@ -51,10 +52,12 @@ services: MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH" DISABLE_ANONYMOUS: "$DISABLE_ANONYMOUS" OPID_LOGIN_SCREEN_PROVIDER: "$OPID_LOGIN_SCREEN_PROVIDER" + LIVE_RELOAD: "$LIVE_RELOAD:-true" command: yarn run start volumes: - ./front:/usr/src/app labels: + - "traefik.enable=true" - "traefik.http.routers.front.rule=Host(`play.workadventure.localhost`)" - "traefik.http.routers.front.entryPoints=web" - "traefik.http.services.front.loadbalancer.server.port=8080" @@ -87,6 +90,7 @@ services: volumes: - ./pusher:/usr/src/app labels: + - "traefik.enable=true" - "traefik.http.routers.pusher.rule=Host(`pusher.workadventure.localhost`)" - "traefik.http.routers.pusher.entryPoints=web" - "traefik.http.services.pusher.loadbalancer.server.port=8080" @@ -111,6 +115,7 @@ services: volumes: - ./maps:/var/www/html labels: + - "traefik.enable=true" - "traefik.http.routers.maps.rule=Host(`maps.workadventure.localhost`)" - "traefik.http.routers.maps.entryPoints=web,traefik" - "traefik.http.services.maps.loadbalancer.server.port=80" @@ -142,6 +147,7 @@ services: volumes: - ./back:/usr/src/app labels: + - "traefik.enable=true" - "traefik.http.routers.back.rule=Host(`api.workadventure.localhost`)" - "traefik.http.routers.back.entryPoints=web" - "traefik.http.services.back.loadbalancer.server.port=8080" @@ -160,6 +166,7 @@ services: volumes: - ./uploader:/usr/src/app labels: + - "traefik.enable=true" - "traefik.http.routers.uploader.rule=Host(`uploader.workadventure.localhost`)" - "traefik.http.routers.uploader.entryPoints=web" - "traefik.http.services.uploader.loadbalancer.server.port=8080" @@ -187,6 +194,7 @@ services: redisinsight: image: redislabs/redisinsight:latest labels: + - "traefik.enable=true" - "traefik.http.routers.redisinsight.rule=Host(`redis.workadventure.localhost`)" - "traefik.http.routers.redisinsight.entryPoints=web" - "traefik.http.services.redisinsight.loadbalancer.server.port=8001" @@ -198,6 +206,7 @@ services: icon: image: matthiasluedtke/iconserver:v3.13.0 labels: + - "traefik.enable=true" - "traefik.http.routers.icon.rule=Host(`icon.workadventure.localhost`)" - "traefik.http.routers.icon.entryPoints=web" - "traefik.http.services.icon.loadbalancer.server.port=8080" diff --git a/front/webpack.config.ts b/front/webpack.config.ts index 7a189911..77ad92bd 100644 --- a/front/webpack.config.ts +++ b/front/webpack.config.ts @@ -32,6 +32,7 @@ module.exports = { rewrites: [{ from: /^_\/.*$/, to: "/index.html" }], disableDotRule: true, }, + liveReload: process.env.LIVE_RELOAD != "0" && process.env.LIVE_RELOAD != "false", }, module: { rules: [ From 78ee4009c84964f1ea7111954e7a0ac63674dcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 09:28:30 +0100 Subject: [PATCH 08/42] Improving tests, WIP --- CONTRIBUTING.md | 40 ++++++++- front/src/Phaser/Login/EntryScene.ts | 9 ++ .../Phaser/Reconnecting/ReconnectingScene.ts | 2 +- tests/.testcaferc.js | 12 ++- tests/package-lock.json | 43 +++++++++- tests/package.json | 3 +- tests/tests/reconnect.ts | 2 +- tests/tests/utils/containers.ts | 82 +++++++++++++++++-- tests/tests/utils/debug.ts | 17 ++++ tests/tests/utils/roles.ts | 11 +++ tests/tests/variables.ts | 80 ++++++++++++++++-- 11 files changed, 276 insertions(+), 25 deletions(-) create mode 100644 tests/tests/utils/debug.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8bbbc93e..2cf9435e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,9 +59,43 @@ $ docker-compose exec back yarn run pretty WorkAdventure is based on a video game engine (Phaser), and video games are not the easiest programs to unit test. -Nevertheless, if your code can be unit tested, please provide a unit test (we use Jasmine). +Nevertheless, if your code can be unit tested, please provide a unit test (we use Jasmine), or an end-to-end test (we use Testcafe). If you are providing a new feature, you should setup a test map in the `maps/tests` directory. The test map should contain -some description text describing how to test the feature. Finally, you should modify the `maps/tests/index.html` file -to add a reference to your newly created test map. +some description text describing how to test the feature. +* if the features is meant to be manually tested, you should modify the `maps/tests/index.html` file to add a reference + to your newly created test map +* if the features can be automatically tested, please provide a testcafe test + +#### Running testcafe tests + +End-to-end tests are available in the "/tests" directory. + +To run these tests locally: + +```console +$ LIVE_RELOAD=0 docker-compose up -d +$ cd tests +$ npm install +$ npm run test +``` + +Note: If your tests fail on a Javascript error in "sockjs", this is due to the +Webpack live reload. The Webpack live reload feature is conflicting with testcafe. This is why we recommend starting +WorkAdventure with the `LIVE_RELOAD=0` environment variable. + +End-to-end tests can take a while to run. To run only one test, use: + +```console +$ npm run test -- tests/[name of the test file].ts +``` + +You can also run the tests inside a container (but you will not have visual feedbacks on your test, so we recommend using +the local tests). + +```console +$ LIVE_RELOAD=0 docker-compose up -d +# Wait 2-3 minutes for the environment to start, then: +$ docker-compose -f docker-compose.testcafe.yaml up +``` diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index 63181ae9..3fb2e6b5 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -3,6 +3,7 @@ import { Scene } from "phaser"; import { ErrorScene, ErrorSceneName } from "../Reconnecting/ErrorScene"; import { WAError } from "../Reconnecting/WAError"; import { waScaleManager } from "../Services/WaScaleManager"; +import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene"; export const EntrySceneName = "EntryScene"; @@ -17,6 +18,14 @@ export class EntryScene extends Scene { }); } + // From the very start, let's preload images used in the ReconnectingScene. + preload() { + this.load.image(ReconnectingTextures.icon, "static/images/favicons/favicon-32x32.png"); + // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap + this.load.bitmapFont(ReconnectingTextures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); + } + create() { gameManager .init(this.scene) diff --git a/front/src/Phaser/Reconnecting/ReconnectingScene.ts b/front/src/Phaser/Reconnecting/ReconnectingScene.ts index 3c8a966c..f02ca75a 100644 --- a/front/src/Phaser/Reconnecting/ReconnectingScene.ts +++ b/front/src/Phaser/Reconnecting/ReconnectingScene.ts @@ -3,7 +3,7 @@ import Image = Phaser.GameObjects.Image; import Sprite = Phaser.GameObjects.Sprite; export const ReconnectingSceneName = "ReconnectingScene"; -enum ReconnectingTextures { +export enum ReconnectingTextures { icon = "icon", mainFont = "main_font", } diff --git a/tests/.testcaferc.js b/tests/.testcaferc.js index 9f7628c5..8011038c 100644 --- a/tests/.testcaferc.js +++ b/tests/.testcaferc.js @@ -3,7 +3,6 @@ const BROWSER = process.env.BROWSER || "chrome --use-fake-ui-for-media-stream -- module.exports = { "browsers": BROWSER, "hostname": "localhost", - //"skipJsErrors": true, "src": "tests/", "screenshots": { "path": "screenshots/", @@ -12,4 +11,15 @@ module.exports = { }, "assertionTimeout": 10000, "selectorTimeout": 40000, + + /*"skipJsErrors": true, + "clientScripts": [ { "content": ` + window.addEventListener('error', function (e) { + if (e instanceof Error && e.message.includes('_jp')) { + console.log('Ignoring sockjs related error'); + return; + } + throw e; + }); + ` } ]*/ } diff --git a/tests/package-lock.json b/tests/package-lock.json index fc3cf124..2fb66e00 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -5,7 +5,8 @@ "packages": { "": { "dependencies": { - "@types/dockerode": "^3.3.0" + "@types/dockerode": "^3.3.0", + "axios": "^0.24.0" }, "devDependencies": { "dockerode": "^3.3.1", @@ -2095,6 +2096,14 @@ "node": ">= 4.5.0" } }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, "node_modules/babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -3031,6 +3040,25 @@ "node": ">=6" } }, + "node_modules/follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -6843,6 +6871,14 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "requires": { + "follow-redirects": "^1.14.4" + } + }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -7594,6 +7630,11 @@ "locate-path": "^3.0.0" } }, + "follow-redirects": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", + "integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", diff --git a/tests/package.json b/tests/package.json index f36c5ea4..a21a3881 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,6 +7,7 @@ "test": "testcafe" }, "dependencies": { - "@types/dockerode": "^3.3.0" + "@types/dockerode": "^3.3.0", + "axios": "^0.24.0" } } diff --git a/tests/tests/reconnect.ts b/tests/tests/reconnect.ts index b4a052c5..fa7352da 100644 --- a/tests/tests/reconnect.ts +++ b/tests/tests/reconnect.ts @@ -4,7 +4,7 @@ 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"; +import {findContainer, rebootBack, rebootPusher, resetRedis, startContainer, stopContainer} from "./utils/containers"; fixture `Reconnection` .page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json`; diff --git a/tests/tests/utils/containers.ts b/tests/tests/utils/containers.ts index 6767b6dc..454340ff 100644 --- a/tests/tests/utils/containers.ts +++ b/tests/tests/utils/containers.ts @@ -1,7 +1,9 @@ //import Docker from "dockerode"; //import * as Dockerode from "dockerode"; import Dockerode = require( 'dockerode') - +const util = require('util'); +const exec = util.promisify(require('child_process').exec); +const { execSync } = require('child_process'); /** * Returns a container ID based on the container name. @@ -33,19 +35,81 @@ export async function startContainer(container: Dockerode.ContainerInfo): Promis } export async function rebootBack(): Promise { - const container = await findContainer('back'); + let stdout = execSync('docker-compose up --force-recreate -d back', { + cwd: __dirname + '/../../../' + }); + /*const container = await findContainer('back'); await stopContainer(container); - await startContainer(container); + await startContainer(container);*/ +} + +export function rebootTraefik(): void { + let stdout = execSync('docker-compose up --force-recreate -d reverse-proxy', { + cwd: __dirname + '/../../../' + }); + + //console.log('rebootTraefik', stdout); } export async function rebootPusher(): Promise { - const container = await findContainer('pusher'); + let stdout = execSync('docker-compose up --force-recreate -d pusher', { + cwd: __dirname + '/../../../' + }); + /*const container = await findContainer('pusher'); await stopContainer(container); - await startContainer(container); + await startContainer(container);*/ } -export async function rebootRedis(): Promise { - const container = await findContainer('redis'); - await stopContainer(container); - await startContainer(container); +export async function resetRedis(): Promise { + let stdout = execSync('docker-compose stop redis', { + cwd: __dirname + '/../../../' + }); + //console.log('rebootRedis', stdout); + + stdout = execSync('docker-compose rm -f redis', { + cwd: __dirname + '/../../../' + }); + //console.log('rebootRedis', stdout); + + stdout = execSync('docker-compose up --force-recreate -d redis', { + cwd: __dirname + '/../../../' + }); + + //console.log('rebootRedis', stdout); +/* + let stdout = execSync('docker-compose stop redis', { + cwd: __dirname + '/../../../' + }); + console.log('stdout:', stdout); + stdout = execSync('docker-compose rm redis', { + cwd: __dirname + '/../../../' + }); + //const { stdout, stderr } = await exec('docker-compose down redis'); + console.log('stdout:', stdout); + //console.log('stderr:', stderr); + const { stdout2, stderr2 } = await exec('docker-compose up -d redis'); + console.log('stdout:', stdout2); + console.log('stderr:', stderr2); +*/ + /*const container = await findContainer('redis'); + //await stopContainer(container); + //await startContainer(container); + + const docker = new Dockerode(); + await docker.getContainer(container.Id).stop(); + await docker.getContainer(container.Id).remove(); + const newContainer = await docker.createContainer(container); + await newContainer.start();*/ +} + +export function stopRedis(): void { + let stdout = execSync('docker-compose stop redis', { + cwd: __dirname + '/../../../' + }); +} + +export function startRedis(): void { + let stdout = execSync('docker-compose start redis', { + cwd: __dirname + '/../../../' + }); } diff --git a/tests/tests/utils/debug.ts b/tests/tests/utils/debug.ts new file mode 100644 index 00000000..c29ac5b0 --- /dev/null +++ b/tests/tests/utils/debug.ts @@ -0,0 +1,17 @@ +import axios from "axios"; + +export async function getPusherDump(): Promise { + return (await axios({ + url: 'http://pusher.workadventure.localhost/dump?token=123', + method: 'get', + })).data; +} + + +export async function getBackDump(): Promise { + return (await axios({ + url: 'http://api.workadventure.localhost/dump?token=123', + method: 'get', + })).data; +} + diff --git a/tests/tests/utils/roles.ts b/tests/tests/utils/roles.ts index 7ed4cad7..5282e564 100644 --- a/tests/tests/utils/roles.ts +++ b/tests/tests/utils/roles.ts @@ -1,5 +1,16 @@ import { Role } from 'testcafe'; +export const userAliceOnPage = (url: string) => Role(url, async t => { + await t + .typeText('input[name="loginSceneName"]', 'Alice') + .click('button.loginSceneFormSubmit') + .click('button.selectCharacterButtonRight') + .click('button.selectCharacterButtonRight') + .click('button.selectCharacterSceneFormSubmit') + .click('button.letsgo'); +}); + + export const userAlice = Role('http://play.workadventure.localhost/', async t => { await t .typeText('input[name="loginSceneName"]', 'Alice') diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index a3d16fda..1e3a516d 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -3,8 +3,17 @@ 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"; +import {userAlice, userAliceOnPage} from "./utils/roles"; +import { + findContainer, + rebootBack, + rebootPusher, + resetRedis, + rebootTraefik, + startContainer, + stopContainer, stopRedis, startRedis +} from "./utils/containers"; +import {getPusherDump} from "./utils/debug"; // Note: we are also testing that passing a random query parameter does not cause any issue. fixture `Variables` @@ -14,21 +23,75 @@ test("Test that variables storage works", async (t: TestController) => { const variableInput = Selector('#textField'); + await resetRedis(); + await Promise.all([ rebootBack(), - rebootRedis(), rebootPusher(), ]); - await t.useRole(userAlice) + //const mainWindow = await t.getCurrentWindow(); + + await t.useRole(userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1')) .switchToIframe("#cowebsite-buffer iframe") .expect(variableInput.value).eql('default value') + .selectText(variableInput) + .pressKey('delete') .typeText(variableInput, 'new value') - .switchToPreviousWindow() + .pressKey('tab') + .switchToMainWindow() + //.switchToWindow(mainWindow) + .wait(500) // reload - /*.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') .switchToIframe("#cowebsite-buffer iframe") - .expect(variableInput.value).eql('new value')*/ + .expect(variableInput.value).eql('new value') + //.debug() + .switchToMainWindow() + //.switchToWindow(mainWindow) +/* + .wait(5000) + //.debug() + .switchToIframe("#cowebsite-buffer iframe") + .expect(variableInput.value).eql('new value') + .switchToMainWindow();*/ + + // Now, let's kill the reverse proxy to cut the connexion + //console.log('Rebooting traefik'); + await rebootTraefik(); + //console.log('Rebooting done'); + + + await t + .switchToIframe("#cowebsite-buffer iframe") + .expect(variableInput.value).eql('new value') + + stopRedis(); + + // Redis is stopped, let's try to modify a variable. + await t.selectText(variableInput) + .pressKey('delete') + .typeText(variableInput, 'value set while Redis stopped') + .pressKey('tab') + .switchToMainWindow() + + startRedis(); + + // Navigate to some other map so that the pusher connection is freed. + await t.navigateTo('http://maps.workadventure.localhost/tests/'); + + // TODO: check that Back and Pusher rooms are unloaded + // TODO: check that Back and Pusher rooms are unloaded + // TODO: check that Back and Pusher rooms are unloaded + // TODO: check that Back and Pusher rooms are unloaded + console.log(await getPusherDump()); + + await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .switchToIframe("#cowebsite-buffer iframe") + // Redis will reconnect automatically and will store the variable on reconnect! + // So we should see the new value. + .expect(variableInput.value).eql('value set while Redis stopped') + .switchToMainWindow() @@ -50,7 +113,7 @@ test("Test that variables storage works", 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. fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json'); @@ -77,3 +140,4 @@ test("Test that variables cache in the back don't prevent setting a variable in } }); +*/ From c89afba6cd4011dac2f35282f57c9aabebabf7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 15:21:31 +0100 Subject: [PATCH 09/42] Working on more tests --- .../Phaser/Reconnecting/ReconnectingScene.ts | 2 +- tests/tests/variables.ts | 63 ++++++++++++++++--- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/front/src/Phaser/Reconnecting/ReconnectingScene.ts b/front/src/Phaser/Reconnecting/ReconnectingScene.ts index f02ca75a..aa19bd21 100644 --- a/front/src/Phaser/Reconnecting/ReconnectingScene.ts +++ b/front/src/Phaser/Reconnecting/ReconnectingScene.ts @@ -41,7 +41,7 @@ export class ReconnectingScene extends Phaser.Scene { "Connection lost. Reconnecting..." ); - const cat = this.physics.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat"); + const cat = this.add.sprite(this.game.renderer.width / 2, this.game.renderer.height / 2 - 32, "cat"); this.anims.create({ key: "right", frames: this.anims.generateFrameNumbers("cat", { start: 6, end: 8 }), diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index 1e3a516d..7edfb072 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -13,7 +13,7 @@ import { startContainer, stopContainer, stopRedis, startRedis } from "./utils/containers"; -import {getPusherDump} from "./utils/debug"; +import {getBackDump, getPusherDump} from "./utils/debug"; // Note: we are also testing that passing a random query parameter does not cause any issue. fixture `Variables` @@ -78,13 +78,21 @@ test("Test that variables storage works", async (t: TestController) => { startRedis(); // Navigate to some other map so that the pusher connection is freed. - await t.navigateTo('http://maps.workadventure.localhost/tests/'); + await t.navigateTo('http://maps.workadventure.localhost/tests/') + .wait(3000); - // TODO: check that Back and Pusher rooms are unloaded - // TODO: check that Back and Pusher rooms are unloaded - // TODO: check that Back and Pusher rooms are unloaded - // TODO: check that Back and Pusher rooms are unloaded - console.log(await getPusherDump()); + + const backDump = await getBackDump(); + //console.log('backDump', backDump); + for (const room of backDump) { + if (room.roomUrl === 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') { + throw new Error('Room still found in back'); + } + } + + const pusherDump = await getPusherDump(); + //console.log('pusherDump', pusherDump); + await t.expect(pusherDump['http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json']).eql(undefined); await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') .switchToIframe("#cowebsite-buffer iframe") @@ -94,6 +102,43 @@ test("Test that variables storage works", async (t: TestController) => { .switchToMainWindow() + // Now, let's try to kill / reboot the back + await rebootBack(); + + await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .switchToIframe("#cowebsite-buffer iframe") + .expect(variableInput.value).eql('value set while Redis stopped') + .selectText(variableInput) + .pressKey('delete') + .typeText(variableInput, 'value set after back restart') + .pressKey('tab') + .switchToMainWindow() + + .navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .switchToIframe("#cowebsite-buffer iframe") + // Redis will reconnect automatically and will store the variable on reconnect! + // So we should see the new value. + .expect(variableInput.value).eql('value set after back restart') + .switchToMainWindow() + + // Now, let's try to kill / reboot the back + await rebootPusher(); + + await t.navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .switchToIframe("#cowebsite-buffer iframe") + .expect(variableInput.value).eql('value set after back restart') + .selectText(variableInput) + .pressKey('delete') + .typeText(variableInput, 'value set after pusher restart') + .pressKey('tab') + .switchToMainWindow() + + .navigateTo('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json') + .switchToIframe("#cowebsite-buffer iframe") + // Redis will reconnect automatically and will store the variable on reconnect! + // So we should see the new value. + .expect(variableInput.value).eql('value set after pusher restart') + .switchToMainWindow() /* @@ -113,7 +158,7 @@ test("Test that variables storage works", 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. fs.copyFileSync('../maps/tests/Variables/Cache/variables_cache_1.json', '../maps/tests/Variables/Cache/variables_tmp.json'); @@ -140,4 +185,4 @@ test("Test that variables cache in the back don't prevent setting a variable in } }); -*/ + From 0a3417e813dbff01955b30ed446b162a47e1a057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 18:20:32 +0100 Subject: [PATCH 10/42] Adding a Docker image to have testcafe + docker-compose --- docker-compose.testcafe.yml | 8 +++++--- tests/Dockerfile | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 tests/Dockerfile diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index bbd5e5ff..86e7d0a3 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -1,12 +1,14 @@ version: "3" services: testcafe: - image: testcafe/testcafe:1.17.1 - working_dir: /tests + build: tests/ + working_dir: /project/tests + # Run as root to have the right to access /var/run/docker.sock + user: root environment: BROWSER: "chromium --use-fake-device-for-media-stream" volumes: - - ./tests:/tests + - ./:/project - ./maps:/maps - /var/run/docker.sock:/var/run/docker.sock # security_opt: diff --git a/tests/Dockerfile b/tests/Dockerfile new file mode 100644 index 00000000..642faed6 --- /dev/null +++ b/tests/Dockerfile @@ -0,0 +1,5 @@ +FROM testcafe/testcafe:1.17.1 + +USER root +RUN apk add docker-compose +USER user From 6427d85c244a9accdf238a96bd9c77adca6c1620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 18:29:36 +0100 Subject: [PATCH 11/42] Fixing warning in lint --- back/src/Controller/DebugController.ts | 75 ++++++++++++++------------ 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts index 5210667b..160a2342 100644 --- a/back/src/Controller/DebugController.ts +++ b/back/src/Controller/DebugController.ts @@ -11,44 +11,51 @@ export class DebugController { } getDump() { - this.App.get("/dump", async (res: HttpResponse, req: HttpRequest) => { - const query = parse(req.getQuery()); + this.App.get("/dump", (res: HttpResponse, req: HttpRequest) => { + (async () => { + const query = parse(req.getQuery()); - if (query.token !== ADMIN_API_TOKEN) { - return res.writeStatus("401 Unauthorized").end("Invalid token sent!"); - } + if (query.token !== ADMIN_API_TOKEN) { + return res.writeStatus("401 Unauthorized").end("Invalid token sent!"); + } - return res - .writeStatus("200 OK") - .writeHeader("Content-Type", "application/json") - .end( - stringify(await Promise.all(socketManager.getWorlds().values()), (key: unknown, value: unknown) => { - if (key === "listeners") { - return "Listeners"; - } - if (key === "socket") { - return "Socket"; - } - if (key === "batchedMessages") { - return "BatchedMessages"; - } - if (value instanceof Map) { - const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any - for (const [mapKey, mapValue] of value.entries()) { - obj[mapKey] = mapValue; + return res + .writeStatus("200 OK") + .writeHeader("Content-Type", "application/json") + .end( + stringify(await Promise.all(socketManager.getWorlds().values()), (key: unknown, value: unknown) => { + if (key === "listeners") { + return "Listeners"; } - return obj; - } else if (value instanceof Set) { - const obj: Array = []; - for (const [setKey, setValue] of value.entries()) { - obj.push(setValue); + if (key === "socket") { + return "Socket"; } - return obj; - } else { - return value; - } - }) - ); + if (key === "batchedMessages") { + return "BatchedMessages"; + } + if (value instanceof Map) { + const obj: any = {}; // eslint-disable-line @typescript-eslint/no-explicit-any + for (const [mapKey, mapValue] of value.entries()) { + obj[mapKey] = mapValue; + } + return obj; + } else if (value instanceof Set) { + const obj: Array = []; + for (const [setKey, setValue] of value.entries()) { + obj.push(setValue); + } + return obj; + } else { + return value; + } + }) + ); + + })().catch(e => { + console.error(e); + res.writeStatus("500"); + res.end("An error occurred"); + }); }); } } From 0c7aedb777887ecae8d0e76cf12c187d19003250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 18:41:21 +0100 Subject: [PATCH 12/42] Setting LIVE_RELOAD=0 in CI --- .github/workflows/end_to_end_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index 0e7e61b7..e7dd18e5 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -36,7 +36,7 @@ jobs: run: sudo chown 1000:1000 -R . - name: "Start environment" - run: docker-compose up -d + run: LIVE_RELOAD=0 docker-compose up -d - name: "Wait for environment to build (and downloading testcafe image)" run: (docker-compose -f docker-compose.testcafe.yml pull &) && docker-compose logs -f --tail=0 front | grep -q "Compiled successfully" From f90bd85ded84a7e1e8a5329ad3107c7a196c30dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 19:54:34 +0100 Subject: [PATCH 13/42] Improving error reporting and adding Video reports --- front/src/Phaser/Reconnecting/ErrorScene.ts | 10 +- tests/.testcaferc.js | 5 + tests/package-lock.json | 181 ++++++++++++++++++++ tests/package.json | 1 + 4 files changed, 193 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index 6477b918..d244d5f1 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -4,6 +4,7 @@ import Sprite = Phaser.GameObjects.Sprite; import Text = Phaser.GameObjects.Text; import ScenePlugin = Phaser.Scenes.ScenePlugin; import { WAError } from "./WAError"; +import Axios from "axios"; export const ErrorSceneName = "ErrorScene"; enum Textures { @@ -71,8 +72,7 @@ export class ErrorScene extends Phaser.Scene { /** * Displays the error page, with an error message matching the "error" parameters passed in. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public static showError(error: any, scene: ScenePlugin): void { + public static showError(error: unknown, scene: ScenePlugin): void { console.error(error); if (typeof error === "string" || error instanceof String) { @@ -86,9 +86,10 @@ export class ErrorScene extends Phaser.Scene { subTitle: error.subTitle, message: error.details, }); - } else if (error.response) { + } else if (Axios.isAxiosError(error) && error.response) { // Axios HTTP error // client received an error response (5xx, 4xx) + console.error("Axios error", error.toJSON()); scene.start(ErrorSceneName, { title: "HTTP " + @@ -98,9 +99,10 @@ export class ErrorScene extends Phaser.Scene { subTitle: "An error occurred while accessing URL:", message: error.response.config.url, }); - } else if (error.request) { + } else if (Axios.isAxiosError(error)) { // Axios HTTP error // client never received a response, or request never left + console.error("Axios error", error.toJSON()); scene.start(ErrorSceneName, { title: "Network error", subTitle: error.message, diff --git a/tests/.testcaferc.js b/tests/.testcaferc.js index 8011038c..bf42a985 100644 --- a/tests/.testcaferc.js +++ b/tests/.testcaferc.js @@ -12,6 +12,11 @@ module.exports = { "assertionTimeout": 10000, "selectorTimeout": 40000, + + "videoPath": "screenshots/videos", + "videoOptions": { + "failedOnly": true, + } /*"skipJsErrors": true, "clientScripts": [ { "content": ` window.addEventListener('error', function (e) { diff --git a/tests/package-lock.json b/tests/package-lock.json index 2fb66e00..65b53bf4 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@ffmpeg-installer/ffmpeg": "^1.1.0", "@types/dockerode": "^3.3.0", "axios": "^0.24.0" }, @@ -1809,6 +1810,123 @@ "node": ">=6.9.0" } }, + "node_modules/@ffmpeg-installer/darwin-arm64": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz", + "integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffmpeg-installer/darwin-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz", + "integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ffmpeg-installer/ffmpeg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz", + "integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==", + "optionalDependencies": { + "@ffmpeg-installer/darwin-arm64": "4.1.5", + "@ffmpeg-installer/darwin-x64": "4.1.0", + "@ffmpeg-installer/linux-arm": "4.1.3", + "@ffmpeg-installer/linux-arm64": "4.1.4", + "@ffmpeg-installer/linux-ia32": "4.1.0", + "@ffmpeg-installer/linux-x64": "4.1.0", + "@ffmpeg-installer/win32-ia32": "4.1.0", + "@ffmpeg-installer/win32-x64": "4.1.0" + } + }, + "node_modules/@ffmpeg-installer/linux-arm": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz", + "integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==", + "cpu": [ + "arm" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz", + "integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==", + "cpu": [ + "arm64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz", + "integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==", + "cpu": [ + "ia32" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/linux-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz", + "integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==", + "cpu": [ + "x64" + ], + "hasInstallScript": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@ffmpeg-installer/win32-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz", + "integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ffmpeg-installer/win32-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz", + "integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@miherlosev/esm": { "version": "3.2.26", "resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz", @@ -6636,6 +6754,69 @@ "to-fast-properties": "^2.0.0" } }, + "@ffmpeg-installer/darwin-arm64": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz", + "integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==", + "optional": true + }, + "@ffmpeg-installer/darwin-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz", + "integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==", + "optional": true + }, + "@ffmpeg-installer/ffmpeg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz", + "integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==", + "requires": { + "@ffmpeg-installer/darwin-arm64": "4.1.5", + "@ffmpeg-installer/darwin-x64": "4.1.0", + "@ffmpeg-installer/linux-arm": "4.1.3", + "@ffmpeg-installer/linux-arm64": "4.1.4", + "@ffmpeg-installer/linux-ia32": "4.1.0", + "@ffmpeg-installer/linux-x64": "4.1.0", + "@ffmpeg-installer/win32-ia32": "4.1.0", + "@ffmpeg-installer/win32-x64": "4.1.0" + } + }, + "@ffmpeg-installer/linux-arm": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz", + "integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==", + "optional": true + }, + "@ffmpeg-installer/linux-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz", + "integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==", + "optional": true + }, + "@ffmpeg-installer/linux-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz", + "integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==", + "optional": true + }, + "@ffmpeg-installer/linux-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz", + "integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==", + "optional": true + }, + "@ffmpeg-installer/win32-ia32": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz", + "integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==", + "optional": true + }, + "@ffmpeg-installer/win32-x64": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz", + "integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==", + "optional": true + }, "@miherlosev/esm": { "version": "3.2.26", "resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz", diff --git a/tests/package.json b/tests/package.json index a21a3881..0db57f26 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,6 +7,7 @@ "test": "testcafe" }, "dependencies": { + "@ffmpeg-installer/ffmpeg": "^1.1.0", "@types/dockerode": "^3.3.0", "axios": "^0.24.0" } From 8b21ba5383044554a1b4b960c5d72d28d923d32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 20:55:46 +0100 Subject: [PATCH 14/42] Improving Axios error logging --- front/src/Phaser/Reconnecting/ErrorScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index d244d5f1..d91c3421 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -89,7 +89,7 @@ export class ErrorScene extends Phaser.Scene { } else if (Axios.isAxiosError(error) && error.response) { // Axios HTTP error // client received an error response (5xx, 4xx) - console.error("Axios error", error.toJSON()); + console.error("Axios error. Request:", error.request, " - Response: ", error.response); scene.start(ErrorSceneName, { title: "HTTP " + @@ -102,7 +102,7 @@ export class ErrorScene extends Phaser.Scene { } else if (Axios.isAxiosError(error)) { // Axios HTTP error // client never received a response, or request never left - console.error("Axios error", error.toJSON()); + console.error("Axios error. No full HTTP response received. Request:", error.request); scene.start(ErrorSceneName, { title: "Network error", subTitle: error.message, From ebcfc069bcca18532037c3699100f4ef321483f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 21:45:25 +0100 Subject: [PATCH 15/42] More details in the error --- front/src/Phaser/Reconnecting/ErrorScene.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index d91c3421..f66c5f83 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -102,7 +102,12 @@ export class ErrorScene extends Phaser.Scene { } else if (Axios.isAxiosError(error)) { // Axios HTTP error // client never received a response, or request never left - console.error("Axios error. No full HTTP response received. Request:", error.request); + console.error( + "Axios error. No full HTTP response received. Request to URL:", + error.request?.responseURL, + " - Details: ", + error.request?.details + ); scene.start(ErrorSceneName, { title: "Network error", subTitle: error.message, From 487b8317e72d0252fe5dd3d92c2eb76c0c98fb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 21:58:08 +0100 Subject: [PATCH 16/42] Adding stacktrace to error message in console --- front/src/Phaser/Reconnecting/ErrorScene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index f66c5f83..7ba7146f 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -74,6 +74,7 @@ export class ErrorScene extends Phaser.Scene { */ public static showError(error: unknown, scene: ScenePlugin): void { console.error(error); + console.trace(); if (typeof error === "string" || error instanceof String) { scene.start(ErrorSceneName, { From 411ee251ffb1f903a009306d44458bb088540afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 3 Dec 2021 22:02:19 +0100 Subject: [PATCH 17/42] Improving URL displaying of failed Axios request --- front/src/Phaser/Reconnecting/ErrorScene.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index 7ba7146f..1a6ab8c8 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -105,9 +105,7 @@ export class ErrorScene extends Phaser.Scene { // client never received a response, or request never left console.error( "Axios error. No full HTTP response received. Request to URL:", - error.request?.responseURL, - " - Details: ", - error.request?.details + error.config.url ); scene.start(ErrorSceneName, { title: "Network error", From 3028a1b0e46b065151852c5f79304183f787cd2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 00:52:17 +0100 Subject: [PATCH 18/42] Trying restart instead in up --force-recreate to have pusher running again --- tests/tests/utils/containers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tests/utils/containers.ts b/tests/tests/utils/containers.ts index 454340ff..9d94c648 100644 --- a/tests/tests/utils/containers.ts +++ b/tests/tests/utils/containers.ts @@ -35,7 +35,7 @@ export async function startContainer(container: Dockerode.ContainerInfo): Promis } export async function rebootBack(): Promise { - let stdout = execSync('docker-compose up --force-recreate -d back', { + let stdout = execSync('docker-compose restart back', { cwd: __dirname + '/../../../' }); /*const container = await findContainer('back'); @@ -44,7 +44,7 @@ export async function rebootBack(): Promise { } export function rebootTraefik(): void { - let stdout = execSync('docker-compose up --force-recreate -d reverse-proxy', { + let stdout = execSync('docker-compose restart reverse-proxy', { cwd: __dirname + '/../../../' }); @@ -52,7 +52,7 @@ export function rebootTraefik(): void { } export async function rebootPusher(): Promise { - let stdout = execSync('docker-compose up --force-recreate -d pusher', { + let stdout = execSync('docker-compose restart pusher', { cwd: __dirname + '/../../../' }); /*const container = await findContainer('pusher'); From 720370a9fa0b2ad7669fbe48dfb80168e76e16ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 10:53:40 +0100 Subject: [PATCH 19/42] Revert "Trying restart instead in up --force-recreate to have pusher running again" This reverts commit 3028a1b0e46b065151852c5f79304183f787cd2e. --- tests/tests/utils/containers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/tests/utils/containers.ts b/tests/tests/utils/containers.ts index 9d94c648..454340ff 100644 --- a/tests/tests/utils/containers.ts +++ b/tests/tests/utils/containers.ts @@ -35,7 +35,7 @@ export async function startContainer(container: Dockerode.ContainerInfo): Promis } export async function rebootBack(): Promise { - let stdout = execSync('docker-compose restart back', { + let stdout = execSync('docker-compose up --force-recreate -d back', { cwd: __dirname + '/../../../' }); /*const container = await findContainer('back'); @@ -44,7 +44,7 @@ export async function rebootBack(): Promise { } export function rebootTraefik(): void { - let stdout = execSync('docker-compose restart reverse-proxy', { + let stdout = execSync('docker-compose up --force-recreate -d reverse-proxy', { cwd: __dirname + '/../../../' }); @@ -52,7 +52,7 @@ export function rebootTraefik(): void { } export async function rebootPusher(): Promise { - let stdout = execSync('docker-compose restart pusher', { + let stdout = execSync('docker-compose up --force-recreate -d pusher', { cwd: __dirname + '/../../../' }); /*const container = await findContainer('pusher'); From da1687bdf22b5666a150e256ecc855e16672f008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 11:01:40 +0100 Subject: [PATCH 20/42] Forcing network name to be able to use docker-compose from inside testcafe container (otherwise, containers started inside testcafe container are not on the same network) --- docker-compose.testcafe.yml | 6 +++++- docker-compose.yaml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index 86e7d0a3..88a4adeb 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -1,4 +1,4 @@ -version: "3" +version: "3.5" services: testcafe: build: tests/ @@ -13,3 +13,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock # security_opt: # - seccomp:unconfined + +networks: + default: + name: workadventure_dev diff --git a/docker-compose.yaml b/docker-compose.yaml index f7563fb9..2eb8aedc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,4 +1,4 @@ -version: "3" +version: "3.5" services: reverse-proxy: image: traefik:v2.5 @@ -239,3 +239,7 @@ services: # #- --cert=/root/letsencrypt/fullchain.pem # #- --pkey=/root/letsencrypt/privkey.pem # network_mode: host + +networks: + default: + name: workadventure_dev From 2cfc504e9a91123c3b26c69e7887113e21c5bc05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 18:18:43 +0100 Subject: [PATCH 21/42] Adding a retry on /anonymLogin --- front/src/Connexion/ConnectionManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index af128028..05d84367 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -10,6 +10,7 @@ import { _ServiceWorker } from "../Network/ServiceWorker"; import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore"; import { userIsConnected } from "../Stores/MenuStore"; import { analyticsClient } from "../Administration/AnalyticsClient"; +import { axiosWithRetry } from "./AxiosUtils"; class ConnectionManager { private localUser!: LocalUser; @@ -232,7 +233,7 @@ class ConnectionManager { } public async anonymousLogin(isBenchmark: boolean = false): Promise { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); + const data = await axiosWithRetry.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); this.localUser = new LocalUser(data.userUuid, [], data.email); this.authToken = data.authToken; if (!isBenchmark) { From 492fd2ed4ec9536b378bfa1732477ef7bddff672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 18:30:12 +0100 Subject: [PATCH 22/42] Adding retried URL in retry error logs --- front/src/Connexion/AxiosUtils.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/front/src/Connexion/AxiosUtils.ts b/front/src/Connexion/AxiosUtils.ts index 190175cf..9d50ce4f 100644 --- a/front/src/Connexion/AxiosUtils.ts +++ b/front/src/Connexion/AxiosUtils.ts @@ -1,6 +1,6 @@ import axios from "axios"; import * as rax from "retry-axios"; -import {errorStore} from "../Stores/ErrorStore"; +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. @@ -15,19 +15,19 @@ axiosWithRetry.defaults.raxConfig = { // You can detect when a retry is happening, and figure out how many // retry attempts have been made - onRetryAttempt: err => { + 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?', { + console.log(err); + console.log(cfg); + console.log(`Retry attempt #${cfg?.currentRetryAttempt} on URL '${err.config.url}'`); + errorStore.addErrorMessage("Unable to connect to WorkAdventure. Are you connected to internet?", { closable: false, - id: "axios_retry" + id: "axios_retry", }); }, }; -axiosWithRetry.interceptors.response.use(res => { +axiosWithRetry.interceptors.response.use((res) => { if (res.status < 400) { errorStore.clearMessageById("axios_retry"); } From a864498d16f62dc3760f65471b2a2797cd4d1aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sat, 4 Dec 2021 23:17:07 +0100 Subject: [PATCH 23/42] Fixing docker-compose up linking to wrong directory when started in container --- CONTRIBUTING.md | 2 +- docker-compose.testcafe.yml | 5 +- docker-compose.yaml | 4 -- tests/tests/utils/containers.ts | 94 +++++++++++---------------------- 4 files changed, 34 insertions(+), 71 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2cf9435e..e5d406cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -97,5 +97,5 @@ the local tests). ```console $ LIVE_RELOAD=0 docker-compose up -d # Wait 2-3 minutes for the environment to start, then: -$ docker-compose -f docker-compose.testcafe.yaml up +$ PROJECT_DIR=$(pwd) docker-compose -f docker-compose.testcafe.yml up ``` diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index 88a4adeb..9c7d5115 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -7,13 +7,10 @@ services: user: root environment: BROWSER: "chromium --use-fake-device-for-media-stream" + PROJECT_DIR: ${PROJECT_DIR} volumes: - ./:/project - ./maps:/maps - /var/run/docker.sock:/var/run/docker.sock # security_opt: # - seccomp:unconfined - -networks: - default: - name: workadventure_dev diff --git a/docker-compose.yaml b/docker-compose.yaml index 2eb8aedc..17e04f7c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -239,7 +239,3 @@ services: # #- --cert=/root/letsencrypt/fullchain.pem # #- --pkey=/root/letsencrypt/privkey.pem # network_mode: host - -networks: - default: - name: workadventure_dev diff --git a/tests/tests/utils/containers.ts b/tests/tests/utils/containers.ts index 454340ff..bed2bd48 100644 --- a/tests/tests/utils/containers.ts +++ b/tests/tests/utils/containers.ts @@ -4,6 +4,30 @@ import Dockerode = require( 'dockerode') const util = require('util'); const exec = util.promisify(require('child_process').exec); const { execSync } = require('child_process'); +const path = require("path"); +const fs = require('fs'); + +/** + * Execute Docker compose, passing the correct host directory (in case this is run from the TestCafe container) + */ +export function dockerCompose(command: string): void { + let param = ''; + const projectDir = process.env.PROJECT_DIR; + + if (!projectDir && fs.existsSync('/project')) { + // We are probably in the docker-image AND we did not pass PROJECT_DIR env variable + throw new Error('Incorrect docker-compose command used to fire testcafe tests. You need to add a PROJECT_DIR environment variable. Please refer to the CONTRIBUTING.md guide'); + } + + if (projectDir) { + const dirName = path.basename(projectDir); + param = '--project-name '+dirName+' --project-directory '+projectDir; + } + + let stdout = execSync('docker-compose '+param+' '+command, { + cwd: __dirname + '/../../../' + }); +} /** * Returns a container ID based on the container name. @@ -35,81 +59,27 @@ export async function startContainer(container: Dockerode.ContainerInfo): Promis } export async function rebootBack(): Promise { - let stdout = execSync('docker-compose up --force-recreate -d back', { - cwd: __dirname + '/../../../' - }); - /*const container = await findContainer('back'); - await stopContainer(container); - await startContainer(container);*/ + dockerCompose('up --force-recreate -d back'); } export function rebootTraefik(): void { - let stdout = execSync('docker-compose up --force-recreate -d reverse-proxy', { - cwd: __dirname + '/../../../' - }); - - //console.log('rebootTraefik', stdout); + dockerCompose('up --force-recreate -d reverse-proxy'); } export async function rebootPusher(): Promise { - let stdout = execSync('docker-compose up --force-recreate -d pusher', { - cwd: __dirname + '/../../../' - }); - /*const container = await findContainer('pusher'); - await stopContainer(container); - await startContainer(container);*/ + dockerCompose('up --force-recreate -d pusher'); } export async function resetRedis(): Promise { - let stdout = execSync('docker-compose stop redis', { - cwd: __dirname + '/../../../' - }); - //console.log('rebootRedis', stdout); - - stdout = execSync('docker-compose rm -f redis', { - cwd: __dirname + '/../../../' - }); - //console.log('rebootRedis', stdout); - - stdout = execSync('docker-compose up --force-recreate -d redis', { - cwd: __dirname + '/../../../' - }); - - //console.log('rebootRedis', stdout); -/* - let stdout = execSync('docker-compose stop redis', { - cwd: __dirname + '/../../../' - }); - console.log('stdout:', stdout); - stdout = execSync('docker-compose rm redis', { - cwd: __dirname + '/../../../' - }); - //const { stdout, stderr } = await exec('docker-compose down redis'); - console.log('stdout:', stdout); - //console.log('stderr:', stderr); - const { stdout2, stderr2 } = await exec('docker-compose up -d redis'); - console.log('stdout:', stdout2); - console.log('stderr:', stderr2); -*/ - /*const container = await findContainer('redis'); - //await stopContainer(container); - //await startContainer(container); - - const docker = new Dockerode(); - await docker.getContainer(container.Id).stop(); - await docker.getContainer(container.Id).remove(); - const newContainer = await docker.createContainer(container); - await newContainer.start();*/ + dockerCompose('stop redis'); + dockerCompose('rm -f redis'); + dockerCompose('up --force-recreate -d redis'); } export function stopRedis(): void { - let stdout = execSync('docker-compose stop redis', { - cwd: __dirname + '/../../../' - }); + dockerCompose('stop redis'); } export function startRedis(): void { - let stdout = execSync('docker-compose start redis', { - cwd: __dirname + '/../../../' - }); + dockerCompose('start redis'); } From 97460886f072a47468b59408fb99f7e7a2f79b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 5 Dec 2021 19:24:54 +0100 Subject: [PATCH 24/42] Fixing CI --- .github/workflows/end_to_end_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index e7dd18e5..3acc6434 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -51,7 +51,7 @@ jobs: # run: docker-compose logs -f pusher | grep -q "WorkAdventure starting on port" - name: "Run tests" - run: docker-compose -f docker-compose.testcafe.yml up --exit-code-from testcafe + run: PROJECT_DIR=$(pwd) docker-compose -f docker-compose.testcafe.yml up --exit-code-from testcafe - name: Upload failed tests if: ${{ failure() }} From a5c7abe61e03338742717ba3370702ced5594f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 5 Dec 2021 21:21:15 +0100 Subject: [PATCH 25/42] Enable testcafe dev mode to pinpoint testcafe errors --- docker-compose.testcafe.yml | 2 ++ tests/tests/variables.ts | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index 9c7d5115..33eb55a6 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -3,6 +3,8 @@ services: testcafe: build: tests/ working_dir: /project/tests + command: + - --dev # Run as root to have the right to access /var/run/docker.sock user: root environment: diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index 7edfb072..6f95873e 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -154,7 +154,11 @@ test("Test that variables storage works", async (t: TestController) => { }).after(async t => { if (!t.ctx.passed) { console.log("Test failed. Browser logs:") - console.log(await t.getBrowserConsoleMessages()); + try { + console.log(await t.getBrowserConsoleMessages()); + } catch (e) { + console.error('Error while fetching browser logs (maybe linked to a closed iframe?)', e); + } } }); From a5325e9327f1e9ff1dea198058b7000fd386da66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 5 Dec 2021 21:24:29 +0100 Subject: [PATCH 26/42] Improving test speed and trying to get error logs whatever happens --- .github/workflows/end_to_end_tests.yml | 2 +- tests/tests/variables.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index 3acc6434..d6370f10 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -39,7 +39,7 @@ jobs: run: LIVE_RELOAD=0 docker-compose up -d - name: "Wait for environment to build (and downloading testcafe image)" - run: (docker-compose -f docker-compose.testcafe.yml pull &) && docker-compose logs -f --tail=0 front | grep -q "Compiled successfully" + run: (docker-compose -f docker-compose.testcafe.yml build &) && docker-compose logs -f --tail=0 front | grep -q "Compiled successfully" # - name: "temp debug: display logs" # run: docker-compose logs diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index 6f95873e..86a6f954 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -158,6 +158,12 @@ test("Test that variables storage works", async (t: TestController) => { console.log(await t.getBrowserConsoleMessages()); } catch (e) { console.error('Error while fetching browser logs (maybe linked to a closed iframe?)', e); + try { + console.log('Logs from main window:'); + console.log(await t.switchToMainWindow().getBrowserConsoleMessages()); + } catch (e) { + console.error('Unable to retrieve logs', e); + } } } }); From 05f805445acb0d581e8be6ffe13f3119a3c36fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 5 Dec 2021 21:33:40 +0100 Subject: [PATCH 27/42] Adding a delay.... who knows! --- tests/tests/variables.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index 86a6f954..f2ad07b5 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -63,6 +63,7 @@ test("Test that variables storage works", async (t: TestController) => { await t + .wait(10000) // Note sure why but maybe this will solve problem in CI/CD env .switchToIframe("#cowebsite-buffer iframe") .expect(variableInput.value).eql('new value') From 94762c86c514652781ef32cc025d86198a3845e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 11:21:18 +0100 Subject: [PATCH 28/42] Refactoring tests --- tests/tests/reconnect.ts | 4 ++-- tests/tests/utils/roles.ts | 36 ++++++++++-------------------------- tests/tests/variables.ts | 37 ++++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/tests/tests/reconnect.ts b/tests/tests/reconnect.ts index fa7352da..62f92fd2 100644 --- a/tests/tests/reconnect.ts +++ b/tests/tests/reconnect.ts @@ -3,7 +3,7 @@ import {assertLogMessage} from "./utils/log"; const fs = require('fs'); const Docker = require('dockerode'); import { Selector } from 'testcafe'; -import {userAlice} from "./utils/roles"; +import {login} from "./utils/roles"; import {findContainer, rebootBack, rebootPusher, resetRedis, startContainer, stopContainer} from "./utils/containers"; fixture `Reconnection` @@ -22,7 +22,7 @@ test("Test that connection can succeed even if WorkAdventure starts while pusher await startContainer(container); - await t.useRole(userAlice); + await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/mousewheel.json'); t.ctx.passed = true; }).after(async t => { diff --git a/tests/tests/utils/roles.ts b/tests/tests/utils/roles.ts index 5282e564..7630f086 100644 --- a/tests/tests/utils/roles.ts +++ b/tests/tests/utils/roles.ts @@ -1,31 +1,15 @@ import { Role } from 'testcafe'; -export const userAliceOnPage = (url: string) => Role(url, async t => { - await t - .typeText('input[name="loginSceneName"]', 'Alice') - .click('button.loginSceneFormSubmit') - .click('button.selectCharacterButtonRight') - .click('button.selectCharacterButtonRight') - .click('button.selectCharacterSceneFormSubmit') - .click('button.letsgo'); -}); +export function login(t: TestController, url: string, userName: string = "Alice", characterNumber: number = 2) { + t = t + .navigateTo(url) + .typeText('input[name="loginSceneName"]', userName) + .click('button.loginSceneFormSubmit'); + for (let i = 0; i < characterNumber; i++) { + t = t.click('button.selectCharacterButtonRight'); + } -export const userAlice = Role('http://play.workadventure.localhost/', async t => { - await t - .typeText('input[name="loginSceneName"]', 'Alice') - .click('button.loginSceneFormSubmit') - .click('button.selectCharacterButtonRight') - .click('button.selectCharacterButtonRight') - .click('button.selectCharacterSceneFormSubmit') + return t.click('button.selectCharacterSceneFormSubmit') .click('button.letsgo'); -}); - -export const userBob = Role('http://play.workadventure.localhost/', async t => { - await t - .typeText('input[name="loginSceneName"]', 'Bob') - .click('button.loginSceneFormSubmit') - .click('button.selectCharacterButtonRight') - .click('button.selectCharacterSceneFormSubmit') - .click('button.letsgo'); -}); +} diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index f2ad07b5..ce377368 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -3,7 +3,7 @@ import {assertLogMessage} from "./utils/log"; const fs = require('fs'); const Docker = require('dockerode'); import { Selector } from 'testcafe'; -import {userAlice, userAliceOnPage} from "./utils/roles"; +import {login} from "./utils/roles"; import { findContainer, rebootBack, @@ -32,7 +32,9 @@ test("Test that variables storage works", async (t: TestController) => { //const mainWindow = await t.getCurrentWindow(); - await t.useRole(userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1')) + login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1'); + + await t //.useRole(userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1')) .switchToIframe("#cowebsite-buffer iframe") .expect(variableInput.value).eql('default value') .selectText(variableInput) @@ -48,6 +50,7 @@ test("Test that variables storage works", async (t: TestController) => { .expect(variableInput.value).eql('new value') //.debug() .switchToMainWindow() + //.wait(5000) //.switchToWindow(mainWindow) /* .wait(5000) @@ -58,12 +61,16 @@ test("Test that variables storage works", async (t: TestController) => { // Now, let's kill the reverse proxy to cut the connexion //console.log('Rebooting traefik'); - await rebootTraefik(); + rebootTraefik(); //console.log('Rebooting done'); + // Maybe we should: + // 1: stop Traefik + // 2: detect reconnecting screen + // 3: start Traefik again + await t - .wait(10000) // Note sure why but maybe this will solve problem in CI/CD env .switchToIframe("#cowebsite-buffer iframe") .expect(variableInput.value).eql('new value') @@ -142,19 +149,10 @@ test("Test that variables storage works", async (t: TestController) => { .switchToMainWindow() - /* - 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("Test 'Test that variables storage works' failed. Browser logs:") try { console.log(await t.getBrowserConsoleMessages()); } catch (e) { @@ -174,7 +172,10 @@ test("Test that variables cache in the back don't prevent setting a variable in // 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); + //const aliceOnPageTmp = userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json'); + + await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json', 'Alice', 2); + //.takeScreenshot('before_switch.png'); // Let's REPLACE the map by a map that has a new variable @@ -182,7 +183,9 @@ test("Test that variables cache in the back don't prevent setting a variable in 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); + + await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json', 'Bob', 3); + //.takeScreenshot('after_switch.png'); // Let's check we successfully manage to save the variable value. @@ -191,7 +194,7 @@ test("Test that variables cache in the back don't prevent setting a variable in t.ctx.passed = true; }).after(async t => { if (!t.ctx.passed) { - console.log("Test failed. Browser logs:") + console.log("Test 'Test that variables cache in the back don't prevent setting a variable in case the map changes' failed. Browser logs:") console.log(await t.getBrowserConsoleMessages()); } }); From f5f69e71436d4889bd2ae999dfe6e2a3890eb457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 11:35:37 +0100 Subject: [PATCH 29/42] Fixing missing await --- tests/tests/variables.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests/variables.ts b/tests/tests/variables.ts index ce377368..6181281b 100644 --- a/tests/tests/variables.ts +++ b/tests/tests/variables.ts @@ -32,7 +32,7 @@ test("Test that variables storage works", async (t: TestController) => { //const mainWindow = await t.getCurrentWindow(); - login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1'); + await login(t, 'http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1'); await t //.useRole(userAliceOnPage('http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/shared_variables.json?somerandomparam=1')) .switchToIframe("#cowebsite-buffer iframe") From a991f0eec9eb0b25e39130326f00e60324f27b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 12:04:04 +0100 Subject: [PATCH 30/42] increase timeouts (who knows....) --- tests/.testcaferc.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/.testcaferc.js b/tests/.testcaferc.js index bf42a985..1c025939 100644 --- a/tests/.testcaferc.js +++ b/tests/.testcaferc.js @@ -9,8 +9,8 @@ module.exports = { "takeOnFails": true, "thumbnails": false, }, - "assertionTimeout": 10000, - "selectorTimeout": 40000, + "assertionTimeout": 20000, + "selectorTimeout": 60000, "videoPath": "screenshots/videos", From 44c4b4fc341b0da7f3cab7e6b16518080ab8b479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 15:14:38 +0100 Subject: [PATCH 31/42] Fixing error handling of fonts when there is no connection to front --- front/src/Phaser/Game/GameScene.ts | 5 +++-- front/src/Phaser/Reconnecting/ErrorScene.ts | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d9bb8186..544387c3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -269,7 +269,9 @@ export class GameScene extends DirtyScene { // 127.0.0.1, localhost and *.localhost are considered secure, even on HTTP. // So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes) // See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure - const url = new URL(file.src); + const base = new URL(window.location.href); + base.pathname = ''; + const url = new URL(file.src, base.toString()); const host = url.host.split(":")[0]; if ( window.location.protocol === "https:" && @@ -322,7 +324,6 @@ export class GameScene extends DirtyScene { this.onMapLoad(data); } - this.load.bitmapFont("main_font", "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); //eslint-disable-next-line @typescript-eslint/no-explicit-any (this.load as any).rexWebFont({ custom: { diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index 1a6ab8c8..82d9b746 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -37,7 +37,11 @@ export class ErrorScene extends Phaser.Scene { preload() { this.load.image(Textures.icon, "static/images/favicons/favicon-32x32.png"); // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap - this.load.bitmapFont(Textures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + if (!this.cache.bitmapFont.has("main_font")) { + // We put this inside a "if" because despite the cache, Phaser will make a query to the XML file. And if there is no connection (which + // is not unlikely given the fact we are in an error scene), this will cause an error. + this.load.bitmapFont(Textures.mainFont, "resources/fonts/arcade.png", "resources/fonts/arcade.xml"); + } this.load.spritesheet("cat", "resources/characters/pipoya/Cat 01-1.png", { frameWidth: 32, frameHeight: 32 }); } From 15565c9a29862f69fb2d3b51f67057b976ed4203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 15:29:24 +0100 Subject: [PATCH 32/42] Changing pusher / back URL depending on environment --- tests/tests/utils/debug.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/tests/utils/debug.ts b/tests/tests/utils/debug.ts index c29ac5b0..50eaa522 100644 --- a/tests/tests/utils/debug.ts +++ b/tests/tests/utils/debug.ts @@ -1,16 +1,29 @@ import axios from "axios"; +const fs = require('fs'); export async function getPusherDump(): Promise { + let url = 'http://pusher.workadventure.localhost/dump?token=123'; + if (fs.existsSync('/project')) { + // We are inside a container. Let's use a direct route + url = 'http://pusher/dump?token=123'; + } + return (await axios({ - url: 'http://pusher.workadventure.localhost/dump?token=123', + url, method: 'get', })).data; } export async function getBackDump(): Promise { + let url = 'http://api.workadventure.localhost/dump?token=123'; + if (fs.existsSync('/project')) { + // We are inside a container. Let's use a direct route + url = 'http://back/dump?token=123'; + } + return (await axios({ - url: 'http://api.workadventure.localhost/dump?token=123', + url, method: 'get', })).data; } From f6803b6d5d74b7af0f93a52b136215260cc0e0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 15:38:30 +0100 Subject: [PATCH 33/42] Fixing pusher and back url --- tests/tests/utils/debug.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests/utils/debug.ts b/tests/tests/utils/debug.ts index 50eaa522..ad065c58 100644 --- a/tests/tests/utils/debug.ts +++ b/tests/tests/utils/debug.ts @@ -5,7 +5,7 @@ export async function getPusherDump(): Promise { let url = 'http://pusher.workadventure.localhost/dump?token=123'; if (fs.existsSync('/project')) { // We are inside a container. Let's use a direct route - url = 'http://pusher/dump?token=123'; + url = 'http://pusher:8080/dump?token=123'; } return (await axios({ @@ -19,7 +19,7 @@ export async function getBackDump(): Promise { let url = 'http://api.workadventure.localhost/dump?token=123'; if (fs.existsSync('/project')) { // We are inside a container. Let's use a direct route - url = 'http://back/dump?token=123'; + url = 'http://back:8080/dump?token=123'; } return (await axios({ From 6cd76b9c1b4a48eb150ced67f6fee85dcc0531a1 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 6 Dec 2021 16:10:35 +0100 Subject: [PATCH 34/42] Add Eslint check & Prettier on Svelte files --- front/{.eslintrc.json => .eslintrc.js} | 18 +++++++++++++++--- front/.prettierrc.json | 3 ++- front/package.json | 16 ++++++++++------ front/yarn.lock | 10 ++++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) rename front/{.eslintrc.json => .eslintrc.js} (75%) diff --git a/front/.eslintrc.json b/front/.eslintrc.js similarity index 75% rename from front/.eslintrc.json rename to front/.eslintrc.js index 45b44456..33466012 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.js @@ -1,4 +1,4 @@ -{ +module.exports = { "root": true, "env": { "browser": true, @@ -18,10 +18,18 @@ "parserOptions": { "ecmaVersion": 2018, "sourceType": "module", - "project": "./tsconfig.json" + "project": "./tsconfig.json", + "extraFileExtensions": [".svelte"] }, "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "svelte3" + ], + "overrides": [ + { + "files": ["*.svelte"], + "processor": "svelte3/svelte3" + } ], "rules": { "no-unused-vars": "off", @@ -34,5 +42,9 @@ "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/restrict-template-expressions": "off" + }, + "settings": { + "svelte3/typescript": true, + "svelte3/ignore-styles": () => true } } diff --git a/front/.prettierrc.json b/front/.prettierrc.json index e8980d15..057ed062 100644 --- a/front/.prettierrc.json +++ b/front/.prettierrc.json @@ -1,4 +1,5 @@ { "printWidth": 120, - "tabWidth": 4 + "tabWidth": 4, + "plugins": ["prettier-plugin-svelte"] } diff --git a/front/package.json b/front/package.json index ec81d8a7..f3ceb211 100644 --- a/front/package.json +++ b/front/package.json @@ -16,6 +16,7 @@ "@typescript-eslint/parser": "^4.23.0", "css-loader": "^5.2.4", "eslint": "^7.26.0", + "eslint-plugin-svelte3": "^3.2.1", "fork-ts-checker-webpack-plugin": "^6.2.9", "html-webpack-plugin": "^5.3.1", "jasmine": "^3.5.0", @@ -24,6 +25,7 @@ "node-polyfill-webpack-plugin": "^1.1.2", "npm-run-all": "^4.1.5", "prettier": "^2.3.1", + "prettier-plugin-svelte": "^2.5.0", "sass": "^1.32.12", "sass-loader": "^11.1.0", "svelte": "^3.38.2", @@ -67,17 +69,19 @@ "build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack", "build-typings": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production BUILD_TYPINGS=1 webpack", "test": "cross-env TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", - "lint": "node_modules/.bin/eslint src/ . --ext .ts", - "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts", + "lint": "node_modules/.bin/eslint src/ . --ext .ts,.svelte", + "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts,.svelte", "precommit": "lint-staged", "svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\" --watch", "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore,a11y-media-has-caption:ignore\"", - "pretty": "yarn prettier --write 'src/**/*.{ts,tsx}'", - "pretty-check": "yarn prettier --check 'src/**/*.{ts,tsx}'" + "pretty": "yarn prettier --write 'src/**/*.{ts,svelte}'", + "pretty-check": "yarn prettier --check 'src/**/*.{ts,svelte}'" }, "lint-staged": { - "*.ts": [ - "prettier --write" + "*.{ts,svelte}": [ + "yarn run svelte-check", + "yarn run fix", + "yarn run pretty" ] } } diff --git a/front/yarn.lock b/front/yarn.lock index f94e659f..f1cbe4b9 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -2040,6 +2040,11 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +eslint-plugin-svelte3@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.1.tgz#f0f24150ecea3061c38c69e282bea26dc3e660c6" + integrity sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q== + eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -4508,6 +4513,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier-plugin-svelte@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1" + integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw== + prettier@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" From c95e20c677937bcf33723e51abade7022bbbe93a Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 6 Dec 2021 16:12:37 +0100 Subject: [PATCH 35/42] Lint & Prettier fixes on Svelte files --- front/src/Components/App.svelte | 97 ++++---- .../AudioManager/AudioManager.svelte | 77 ++++--- front/src/Components/CameraControls.svelte | 39 ++-- front/src/Components/Chat/Chat.svelte | 125 +++++----- front/src/Components/Chat/ChatElement.svelte | 89 ++++---- .../Components/Chat/ChatMessageForm.svelte | 63 ++--- .../src/Components/Chat/ChatPlayerName.svelte | 42 ++-- front/src/Components/Chat/ChatSubMenu.svelte | 26 +-- .../CustomCharacterScene.svelte | 135 ++++++----- .../src/Components/EmoteMenu/EmoteMenu.svelte | 109 +++++---- .../EnableCamera/EnableCameraScene.svelte | 216 +++++++++--------- .../HorizontalSoundMeterWidget.svelte | 50 ++-- .../HelpCameraSettingsPopup.svelte | 31 ++- .../LayoutManager/LayoutManager.svelte | 74 +++--- front/src/Components/Login/LoginScene.svelte | 176 +++++++------- .../Components/Menu/AboutRoomSubMenu.svelte | 101 ++++---- .../Components/Menu/AudioGlobalMessage.svelte | 91 ++++---- .../src/Components/Menu/ContactSubMenu.svelte | 49 ++-- .../src/Components/Menu/CustomSubMenu.svelte | 29 ++- .../Menu/GlobalMessagesSubMenu.svelte | 109 ++++----- front/src/Components/Menu/GuestSubMenu.svelte | 74 +++--- front/src/Components/Menu/Menu.svelte | 176 +++++++------- front/src/Components/Menu/MenuIcon.svelte | 64 +++--- .../src/Components/Menu/ProfileSubMenu.svelte | 173 +++++++------- .../Components/Menu/SettingsSubMenu.svelte | 212 +++++++++-------- .../Components/Menu/TextGlobalMessage.svelte | 49 ++-- front/src/Components/MyCamera.svelte | 27 +-- .../Components/ReportMenu/BlockSubMenu.svelte | 15 +- .../Components/ReportMenu/ReportMenu.svelte | 140 ++++++------ .../ReportMenu/ReportSubMenu.svelte | 32 +-- .../SelectCompanionScene.svelte | 104 +++++---- front/src/Components/SoundMeterWidget.svelte | 34 +-- .../Components/TypeMessage/BanMessage.svelte | 122 +++++----- .../Components/TypeMessage/TextMessage.svelte | 62 ++--- front/src/Components/UI/AudioPlaying.svelte | 54 ++--- front/src/Components/UI/ErrorDialog.svelte | 57 +++-- front/src/Components/Video/ChatLayout.svelte | 20 +- .../Video/LocalStreamMediaBox.svelte | 13 +- front/src/Components/Video/MediaBox.svelte | 12 +- .../Video/PresentationLayout.svelte | 18 +- .../Video/ScreenSharingMediaBox.svelte | 26 +-- .../src/Components/Video/VideoMediaBox.svelte | 29 ++- .../src/Components/Video/VideoOverlay.svelte | 15 +- .../src/Components/VisitCard/VisitCard.svelte | 128 ++++++----- .../WarningContainer/WarningContainer.svelte | 50 ++-- .../SelectCharacterScene.svelte | 113 ++++----- 46 files changed, 1852 insertions(+), 1695 deletions(-) diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index ab8b6d3f..4886cc4e 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -1,147 +1,146 @@
{#if $loginSceneVisibleStore}
- +
{/if} {#if $selectCharacterSceneVisibleStore}
- +
{/if} {#if $customCharacterSceneVisibleStore}
- +
{/if} {#if $selectCompanionSceneVisibleStore}
- +
{/if} {#if $enableCameraSceneVisibilityStore}
- +
{/if} {#if $banMessageVisibleStore}
- +
{/if} {#if $textMessageVisibleStore}
- +
{/if} {#if $soundPlayingStore} -
- -
+
+ +
{/if} {#if $audioManagerVisibilityStore} -
- -
+
+ +
{/if} {#if $layoutManagerVisibilityStore}
- +
{/if} {#if $showReportScreenStore !== userReportEmpty}
- +
{/if} {#if $menuIconVisiblilityStore}
- +
{/if} {#if $menuVisiblilityStore}
- +
{/if} {#if $emoteMenuStore}
- +
{/if} {#if $gameOverlayVisibilityStore}
- - - + + +
{/if} {#if $helpCameraSettingsVisibleStore}
- +
{/if} {#if $requestVisitCardsStore} - + {/if} {#if $errorStore.length > 0} -
- -
+
+ +
{/if} {#if $chatVisibilityStore} - + {/if} {#if $warningContainerStore} - + {/if}
diff --git a/front/src/Components/AudioManager/AudioManager.svelte b/front/src/Components/AudioManager/AudioManager.svelte index e201254c..3253699e 100644 --- a/front/src/Components/AudioManager/AudioManager.svelte +++ b/front/src/Components/AudioManager/AudioManager.svelte @@ -1,10 +1,7 @@ -
- - - + + + + d="M11.536 14.01A8.473 8.473 0 0 0 14.026 8a8.473 8.473 0 0 0-2.49-6.01l-.708.707A7.476 7.476 0 0 1 13.025 8c0 2.071-.84 3.946-2.197 5.303l.708.707z" + /> + d="M10.121 12.596A6.48 6.48 0 0 0 12.025 8a6.48 6.48 0 0 0-1.904-4.596l-.707.707A5.483 5.483 0 0 1 11.025 8a5.483 5.483 0 0 1-1.61 3.89l.706.706z" + /> + d="M8.707 11.182A4.486 4.486 0 0 0 10.025 8a4.486 4.486 0 0 0-1.318-3.182L8 5.525A3.489 3.489 0 0 1 9.025 8 3.49 3.49 0 0 1 8 10.475l.707.707z" + /> - +
+
- \ No newline at end of file + diff --git a/front/src/Components/CameraControls.svelte b/front/src/Components/CameraControls.svelte index 728c84e9..232d42da 100644 --- a/front/src/Components/CameraControls.svelte +++ b/front/src/Components/CameraControls.svelte @@ -1,6 +1,6 @@ - + - -