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 01/35] 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 02/35] 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 03/35] 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 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 04/35] 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 05/35] 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 06/35] 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 07/35] 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 08/35] 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 09/35] 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 10/35] 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 11/35] 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 12/35] 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 13/35] 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 14/35] 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 15/35] 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 16/35] 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 17/35] 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 18/35] 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 19/35] 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 20/35] 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 21/35] 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 22/35] 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 23/35] 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 24/35] 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 25/35] 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 26/35] 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 27/35] 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 28/35] 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 29/35] 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 30/35] 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 31/35] 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 331daca854389fcd55e7ee4e3dfa35b8ec5b4b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 16:37:27 +0100 Subject: [PATCH 32/35] Fixing admin token for requests --- docker-compose.testcafe.yml | 1 + tests/tests/utils/debug.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docker-compose.testcafe.yml b/docker-compose.testcafe.yml index 33eb55a6..e61db21d 100644 --- a/docker-compose.testcafe.yml +++ b/docker-compose.testcafe.yml @@ -10,6 +10,7 @@ services: environment: BROWSER: "chromium --use-fake-device-for-media-stream" PROJECT_DIR: ${PROJECT_DIR} + ADMIN_API_TOKEN: ${ADMIN_API_TOKEN} volumes: - ./:/project - ./maps:/maps diff --git a/tests/tests/utils/debug.ts b/tests/tests/utils/debug.ts index ad065c58..c7cdb592 100644 --- a/tests/tests/utils/debug.ts +++ b/tests/tests/utils/debug.ts @@ -1,11 +1,13 @@ import axios from "axios"; const fs = require('fs'); +const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN; + export async function getPusherDump(): Promise { - let url = 'http://pusher.workadventure.localhost/dump?token=123'; + let url = 'http://pusher.workadventure.localhost/dump?token='+ADMIN_API_TOKEN; if (fs.existsSync('/project')) { // We are inside a container. Let's use a direct route - url = 'http://pusher:8080/dump?token=123'; + url = 'http://pusher:8080/dump?token='+ADMIN_API_TOKEN; } return (await axios({ @@ -14,12 +16,11 @@ export async function getPusherDump(): Promise { })).data; } - export async function getBackDump(): Promise { - let url = 'http://api.workadventure.localhost/dump?token=123'; + let url = 'http://api.workadventure.localhost/dump?token='+ADMIN_API_TOKEN; if (fs.existsSync('/project')) { // We are inside a container. Let's use a direct route - url = 'http://back:8080/dump?token=123'; + url = 'http://back:8080/dump?token='+ADMIN_API_TOKEN; } return (await axios({ From d7875c2c6859803bc948368f74d621ffeff19054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 18:00:17 +0100 Subject: [PATCH 33/35] Adding missing handler for error message in front + removing 2 useless classes in ServerToClientMessage --- front/src/Connexion/RoomConnection.ts | 3 +++ messages/protos/messages.proto | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 08672be1..4a4eea6e 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -255,6 +255,9 @@ export class RoomConnection implements RoomConnection { warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. + } else if (message.hasErrormessage()) { + const errorMessage = message.getErrormessage() as ErrorMessage; + console.error("An error occurred server side: " + errorMessage.getMessage()); } else { throw new Error("Unknown message received"); } diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 76f4f5ad..5b319feb 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -274,12 +274,12 @@ message ServerToClientMessage { SendJitsiJwtMessage sendJitsiJwtMessage = 11; SendUserMessage sendUserMessage = 12; BanUserMessage banUserMessage = 13; - AdminRoomMessage adminRoomMessage = 14; + //AdminRoomMessage adminRoomMessage = 14; WorldFullWarningMessage worldFullWarningMessage = 15; WorldFullMessage worldFullMessage = 16; RefreshRoomMessage refreshRoomMessage = 17; WorldConnexionMessage worldConnexionMessage = 18; - EmoteEventMessage emoteEventMessage = 19; + //EmoteEventMessage emoteEventMessage = 19; TokenExpiredMessage tokenExpiredMessage = 20; } } From 84f7a8c38329444d776a653f738344147525811d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 18:20:06 +0100 Subject: [PATCH 34/35] Pretty fix --- front/src/Connexion/Room.ts | 4 ++-- front/src/Phaser/Game/GameScene.ts | 2 +- front/src/Phaser/Reconnecting/ErrorScene.ts | 5 +--- front/src/Stores/ErrorStore.ts | 26 +++++++++++---------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 9603adf6..4b6d82a4 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -1,10 +1,10 @@ -import * as rax from 'retry-axios'; +import * as rax from "retry-axios"; import Axios from "axios"; import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import type { CharacterTexture } from "./LocalUser"; import { localUserStore } from "./LocalUserStore"; import axios from "axios"; -import {axiosWithRetry} from "./AxiosUtils"; +import { axiosWithRetry } from "./AxiosUtils"; export class MapDetail { constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 544387c3..58ba564e 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -270,7 +270,7 @@ export class GameScene extends DirtyScene { // 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 base = new URL(window.location.href); - base.pathname = ''; + base.pathname = ""; const url = new URL(file.src, base.toString()); const host = url.host.split(":")[0]; if ( diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index 82d9b746..078bee71 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -107,10 +107,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. No full HTTP response received. Request to URL:", - error.config.url - ); + console.error("Axios error. No full HTTP response received. Request to URL:", error.config.url); scene.start(ErrorSceneName, { title: "Network error", subTitle: error.message, diff --git a/front/src/Stores/ErrorStore.ts b/front/src/Stores/ErrorStore.ts index 7db7ed32..3b6d7842 100644 --- a/front/src/Stores/ErrorStore.ts +++ b/front/src/Stores/ErrorStore.ts @@ -1,7 +1,7 @@ -import {derived, writable} from "svelte/store"; +import { derived, writable } from "svelte/store"; interface ErrorMessage { - id: string|undefined; + id: string | undefined; closable: boolean; // Whether it can be closed by a user action or not message: string | number | boolean | undefined; } @@ -14,10 +14,13 @@ function createErrorStore() { return { subscribe, - addErrorMessage: (e: string | Error, options?: { - closable?: boolean, - id?: string - }): void => { + addErrorMessage: ( + e: string | Error, + options?: { + closable?: boolean; + id?: string; + } + ): void => { update((messages: ErrorMessage[]) => { let message: string; if (e instanceof Error) { @@ -26,11 +29,11 @@ function createErrorStore() { message = e; } - if (!messages.find(errorMessage => errorMessage.message === message)) { + if (!messages.find((errorMessage) => errorMessage.message === message)) { messages.push({ message, closable: options?.closable ?? true, - id: options?.id + id: options?.id, }); } @@ -39,13 +42,13 @@ function createErrorStore() { }, clearMessageById: (id: string): void => { update((messages: ErrorMessage[]) => { - messages = messages.filter(message => message.id !== id); + messages = messages.filter((message) => message.id !== id); return messages; }); }, clearClosableMessages: (): void => { update((messages: ErrorMessage[]) => { - messages = messages.filter(message => message.closable); + messages = messages.filter((message) => message.closable); return messages; }); }, @@ -54,8 +57,7 @@ function createErrorStore() { export const errorStore = createErrorStore(); - export const hasClosableMessagesInErrorStore = derived(errorStore, ($errorStore) => { - const closableMessage = $errorStore.find(errorMessage => errorMessage.closable); + const closableMessage = $errorStore.find((errorMessage) => errorMessage.closable); return !!closableMessage; }); From b9840cfa3d579b6b886cba0e6e394ba640ddfc8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 6 Dec 2021 18:34:09 +0100 Subject: [PATCH 35/35] Fix CI --- back/src/Controller/DebugController.ts | 52 +++++++++++++------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/back/src/Controller/DebugController.ts b/back/src/Controller/DebugController.ts index 160a2342..8fbf82e4 100644 --- a/back/src/Controller/DebugController.ts +++ b/back/src/Controller/DebugController.ts @@ -23,35 +23,37 @@ export class DebugController { .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; + 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"; + } + 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; } - return obj; - } else { - return value; } - }) + ) ); - - })().catch(e => { + })().catch((e) => { console.error(e); res.writeStatus("500"); res.end("An error occurred");