Adding end-to-end tests
The first test is testing a tricky cache scenario with the back when testing variables. The end-to-end package used is testcafe.
This commit is contained in:
parent
d8ecae64f0
commit
a82f4e1813
@ -14,6 +14,7 @@ export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4");
|
|||||||
export const REDIS_HOST = process.env.REDIS_HOST || undefined;
|
export const REDIS_HOST = process.env.REDIS_HOST || undefined;
|
||||||
export const REDIS_PORT = parseInt(process.env.REDIS_PORT || "6379") || 6379;
|
export const REDIS_PORT = parseInt(process.env.REDIS_PORT || "6379") || 6379;
|
||||||
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || undefined;
|
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD || undefined;
|
||||||
|
export const STORE_VARIABLES_FOR_LOCAL_MAPS = process.env.STORE_VARIABLES_FOR_LOCAL_MAPS === "true";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
MINIMUM_DISTANCE,
|
MINIMUM_DISTANCE,
|
||||||
|
@ -383,6 +383,7 @@ export class GameRoom {
|
|||||||
|
|
||||||
// Reset the variable manager
|
// Reset the variable manager
|
||||||
this.variableManagerPromise = undefined;
|
this.variableManagerPromise = undefined;
|
||||||
|
this.mapPromise = undefined;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'An error occurred while setting the "' + name + "\" variable. Let's reload the map and try again"
|
'An error occurred while setting the "' + name + "\" variable. Let's reload the map and try again"
|
||||||
|
@ -5,12 +5,13 @@ import { promisify } from "util";
|
|||||||
import { LocalUrlError } from "./LocalUrlError";
|
import { LocalUrlError } from "./LocalUrlError";
|
||||||
import { ITiledMap } from "@workadventure/tiled-map-type-guard";
|
import { ITiledMap } from "@workadventure/tiled-map-type-guard";
|
||||||
import { isTiledMap } from "@workadventure/tiled-map-type-guard/dist";
|
import { isTiledMap } from "@workadventure/tiled-map-type-guard/dist";
|
||||||
|
import { STORE_VARIABLES_FOR_LOCAL_MAPS } from "../Enum/EnvironmentVariable";
|
||||||
|
|
||||||
class MapFetcher {
|
class MapFetcher {
|
||||||
async fetchMap(mapUrl: string): Promise<ITiledMap> {
|
async fetchMap(mapUrl: string): Promise<ITiledMap> {
|
||||||
// Before trying to make the query, let's verify the map is actually on the open internet (and not a local test map)
|
// Before trying to make the query, let's verify the map is actually on the open internet (and not a local test map)
|
||||||
|
|
||||||
if (await this.isLocalUrl(mapUrl)) {
|
if ((await this.isLocalUrl(mapUrl)) && !STORE_VARIABLES_FOR_LOCAL_MAPS) {
|
||||||
throw new LocalUrlError('URL for map "' + mapUrl + '" targets a local map');
|
throw new LocalUrlError('URL for map "' + mapUrl + '" targets a local map');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
docker-compose.testcafe.yml
Normal file
13
docker-compose.testcafe.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
testcafe:
|
||||||
|
image: testcafe/testcafe:1.17.1
|
||||||
|
working_dir: /tests
|
||||||
|
environment:
|
||||||
|
BROWSER: "chromium --use-fake-device-for-media-stream"
|
||||||
|
volumes:
|
||||||
|
- ./tests:/tests
|
||||||
|
- ./tests/.testcaferc.json:/.testcaferc.json
|
||||||
|
- ./maps:/maps
|
||||||
|
# security_opt:
|
||||||
|
# - seccomp:unconfined
|
@ -17,6 +17,12 @@ services:
|
|||||||
- front
|
- front
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases:
|
||||||
|
- 'play.workadventure.localhost'
|
||||||
|
- 'pusher.workadventure.localhost'
|
||||||
|
- 'maps.workadventure.localhost'
|
||||||
|
|
||||||
front:
|
front:
|
||||||
image: thecodingmachine/nodejs:14
|
image: thecodingmachine/nodejs:14
|
||||||
@ -127,6 +133,7 @@ services:
|
|||||||
MAX_PER_GROUP: "MAX_PER_GROUP"
|
MAX_PER_GROUP: "MAX_PER_GROUP"
|
||||||
REDIS_HOST: redis
|
REDIS_HOST: redis
|
||||||
NODE_ENV: development
|
NODE_ENV: development
|
||||||
|
STORE_VARIABLES_FOR_LOCAL_MAPS: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- ./back:/usr/src/app
|
- ./back:/usr/src/app
|
||||||
labels:
|
labels:
|
||||||
|
@ -122,17 +122,22 @@ class LocalUserStore {
|
|||||||
|
|
||||||
setLastRoomUrl(roomUrl: string): void {
|
setLastRoomUrl(roomUrl: string): void {
|
||||||
localStorage.setItem(lastRoomUrl, roomUrl.toString());
|
localStorage.setItem(lastRoomUrl, roomUrl.toString());
|
||||||
|
if ('caches' in window) {
|
||||||
caches.open(cacheAPIIndex).then((cache) => {
|
caches.open(cacheAPIIndex).then((cache) => {
|
||||||
const stringResponse = new Response(JSON.stringify({ roomUrl }));
|
const stringResponse = new Response(JSON.stringify({ roomUrl }));
|
||||||
cache.put(`/${lastRoomUrl}`, stringResponse);
|
cache.put(`/${lastRoomUrl}`, stringResponse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
getLastRoomUrl(): string {
|
getLastRoomUrl(): string {
|
||||||
return (
|
return (
|
||||||
localStorage.getItem(lastRoomUrl) ?? window.location.protocol + "//" + window.location.host + START_ROOM_URL
|
localStorage.getItem(lastRoomUrl) ?? window.location.protocol + "//" + window.location.host + START_ROOM_URL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
getLastRoomUrlCacheApi(): Promise<string | undefined> {
|
getLastRoomUrlCacheApi(): Promise<string | undefined> {
|
||||||
|
if (!('caches' in window)) {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
return caches.open(cacheAPIIndex).then((cache) => {
|
return caches.open(cacheAPIIndex).then((cache) => {
|
||||||
return cache.match(`/${lastRoomUrl}`).then((res) => {
|
return cache.match(`/${lastRoomUrl}`).then((res) => {
|
||||||
return res?.json().then((data) => {
|
return res?.json().then((data) => {
|
||||||
|
1
maps/tests/Variables/Cache/.gitignore
vendored
Normal file
1
maps/tests/Variables/Cache/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
variables_tmp.json
|
5
maps/tests/Variables/Cache/script.js
Normal file
5
maps/tests/Variables/Cache/script.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
WA.onInit().then(async () => {
|
||||||
|
console.log('Trying to set variable "myvar". This should work, even if the cache was busted.');
|
||||||
|
await WA.state.saveVariable('myvar', {'foo': 'bar'});
|
||||||
|
console.log('SUCCESS!');
|
||||||
|
});
|
82
maps/tests/Variables/Cache/variables_cache_1.json
Normal file
82
maps/tests/Variables/Cache/variables_cache_1.json
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ "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,
|
||||||
|
"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:\nThis test is to be run automatically using testcafe",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":2.78125,
|
||||||
|
"y":2.5
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":11,
|
||||||
|
"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
|
||||||
|
}
|
126
maps/tests/Variables/Cache/variables_cache_2.json
Normal file
126
maps/tests/Variables/Cache/variables_cache_2.json
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
{ "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,
|
||||||
|
"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:\nThis test is to be run automatically using testcafe\n\n(2nd file)",
|
||||||
|
"wrap":true
|
||||||
|
},
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":252.4375,
|
||||||
|
"x":2.78125,
|
||||||
|
"y":2.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":9,
|
||||||
|
"name":"myvar",
|
||||||
|
"point":true,
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"default",
|
||||||
|
"type":"string",
|
||||||
|
"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":88.8149900876127,
|
||||||
|
"y":147.75212636695
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":8,
|
||||||
|
"nextobjectid":11,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"script",
|
||||||
|
"type":"string",
|
||||||
|
"value":"script.js"
|
||||||
|
}],
|
||||||
|
"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
|
||||||
|
}
|
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/node_modules
|
12
tests/.testcaferc.js
Normal file
12
tests/.testcaferc.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const BROWSER = process.env.BROWSER || "chrome --use-fake-device-for-media-stream";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"browsers": BROWSER,
|
||||||
|
"hostname": "localhost",
|
||||||
|
"skipJsErrors": true,
|
||||||
|
"src": "tests/",
|
||||||
|
"screenshots": {
|
||||||
|
"path": "screenshots/",
|
||||||
|
"takeOnFails": true
|
||||||
|
}
|
||||||
|
}
|
18
tests/README.md
Normal file
18
tests/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
End-to-end tests
|
||||||
|
|
||||||
|
This directory contains automated end to end tests.
|
||||||
|
|
||||||
|
To run them locally:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ npm install
|
||||||
|
$ npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use docker-compose to run the tests:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker-compose -f docker-compose.testcafe.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: by default, tests are running in Chrome locally and in Chromium in the Docker image.
|
8915
tests/package-lock.json
generated
Normal file
8915
tests/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
8
tests/package.json
Normal file
8
tests/package.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"testcafe": "^1.17.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "testcafe"
|
||||||
|
}
|
||||||
|
}
|
1
tests/screenshots/.gitignore
vendored
Normal file
1
tests/screenshots/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*
|
41
tests/tests/test.ts
Normal file
41
tests/tests/test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
fixture `Variables`
|
||||||
|
.page `http://play.workadventure.localhost/_/global/maps.workadventure.localhost/tests/Variables/Cache/variables_tmp.json`;
|
||||||
|
|
||||||
|
test("Test that variables cache in the back don't prevent setting a variable in case the map changes", async t => {
|
||||||
|
// 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
|
||||||
|
.typeText('input[name="loginSceneName"]', 'foo')
|
||||||
|
.click('button.loginSceneFormSubmit')
|
||||||
|
.click('button.selectCharacterButtonRight')
|
||||||
|
.click('button.selectCharacterButtonRight')
|
||||||
|
.click('button.selectCharacterSceneFormSubmit')
|
||||||
|
.click('button.letsgo');
|
||||||
|
//.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');
|
||||||
|
|
||||||
|
await t.resizeWindow(960, 800);
|
||||||
|
|
||||||
|
await t
|
||||||
|
.typeText('input[name="loginSceneName"]', 'foo')
|
||||||
|
.click('button.loginSceneFormSubmit')
|
||||||
|
.click('button.selectCharacterButtonRight')
|
||||||
|
.click('button.selectCharacterButtonRight')
|
||||||
|
.click('button.selectCharacterSceneFormSubmit')
|
||||||
|
.click('button.letsgo');
|
||||||
|
//.takeScreenshot('after_switch.png');
|
||||||
|
|
||||||
|
const messages = await t.getBrowserConsoleMessages();
|
||||||
|
|
||||||
|
const logs = messages['log'];
|
||||||
|
const lastMessage = logs.pop();
|
||||||
|
// Let's check we successfully manage to save the variable value.
|
||||||
|
await t.expect(lastMessage).eql('SUCCESS!');
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user