V2 e2e playwright (#343)

* test: create utility for uploading test-db

* chore: upgrade deps

* refactor: remove unused functions

* chore: create testing boilerplate

* test: upload showfile
This commit is contained in:
Carlos Valente
2023-04-18 23:16:19 +02:00
committed by GitHub
parent f248b78945
commit b228020b73
11 changed files with 378 additions and 42 deletions
+22
View File
@@ -20,6 +20,28 @@ From the project root, run the following commands
- __Install the project dependencies__ by running `pnpm i`
- __Run dev mode__ by running `turbo dev`
## TESTING
Generally we have 2 types of tests.
- Unit tests for functions that contain business logic
- End-to-end tests for core features
### Unit tests
Unit tests are contained in mostly all the apps and packages (client, server and utils)
You can run unit tests by running turbo `turbo test:pipeline` from the project root.
This will run all tests and close test runner.
Alternatively you can navigate to an app or project and run `pnpm test` to run those tests in watch mode
### E2E tests
E2E tests are in a separate package. On running, [playwright](https://playwright.dev/) will spin up an instance of the webserver to test against
These tests also run against a separate version of the DB (test-db)
You can run playwright tests from project root with `pnpm e2e`
When writing tests, it can be handy to run playwright in interactive mode with `pnpm e2e:i`. You would need to manually start the webserver with `pnpm dev:server`
## CREATE AN INSTALLABLE FILE (Windows | MacOS | Linux)
Ontime uses Electron to distribute the application.
+4 -4
View File
@@ -213,13 +213,13 @@ export const shutdown = async (exitCode = 0) => {
process.on('exit', (code) => console.log(`Ontime exited with code: ${code}`));
process.on('unhandledRejection', async () => {
logger.error('SERVER', 'Error: unhandled rejection');
process.on('unhandledRejection', async (error) => {
logger.error('SERVER', `Error: unhandled rejection ${error}`);
await shutdown(1);
});
process.on('uncaughtException', async () => {
logger.error('SERVER', 'Error: uncaught exception');
process.on('uncaughtException', async (error) => {
logger.error('SERVER', `Error: uncaught exception ${error}`);
await shutdown(1);
});
@@ -288,13 +288,3 @@ export const dbUpload = async (req, res) => {
const file = req.file.path;
await uploadAndParse(file, req, res, options);
};
// Create controller for POST request to '/ontime/dbpath'
// Returns -
export const dbPathToUpload = async (req, res) => {
if (!req.body.path) {
res.status(400).send({ message: 'Path to file not found' });
return;
}
await uploadAndParse(req.body.path, req, res);
};
+3 -7
View File
@@ -2,7 +2,6 @@ import express from 'express';
import { uploadFile } from '../utils/upload.js';
import {
dbDownload,
dbPathToUpload,
dbUpload,
getAliases,
getInfo,
@@ -19,11 +18,11 @@ import {
} from '../controllers/ontimeController.js';
import {
viewValidator,
validateAliases,
validateUserFields,
validateSettings,
validateOSC,
validateSettings,
validateUserFields,
viewValidator,
} from '../controllers/ontimeController.validate.js';
export const router = express.Router();
@@ -69,6 +68,3 @@ router.get('/osc', getOSC);
// create route between controller and '/ontime/osc' endpoint
router.post('/osc', validateOSC, postOSC);
// create route between controller and '/ontime/dbpath' endpoint
router.post('/dbpath', dbPathToUpload);
+90
View File
@@ -0,0 +1,90 @@
{
"rundown": [],
"eventData": {
"title": "All about Carlos demo event",
"publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
"backstageUrl": "www.getontime.no",
"backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject",
"endMessage": ""
},
"settings": {
"app": "ontime",
"version": 2,
"serverPort": 4001,
"lock": null,
"pinCode": "1234",
"timeFormat": "24"
},
"viewSettings": {
"overrideStyles": false
},
"aliases": [
{
"id": "0b0b3",
"enabled": true,
"alias": "test",
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
],
"userFields": {
"user0": "user0",
"user1": "user1",
"user2": "user2",
"user3": "user3",
"user4": "user4",
"user5": "user5",
"user6": "user6",
"user7": "user7",
"user8": "user8",
"user9": "user9"
},
"osc": {
"portIn": 8888,
"portOut": 9999,
"targetIP": "127.0.0.1",
"enabledIn": false,
"enabledOut": false,
"subscriptions": {
"onLoad": [],
"onStart": [],
"onPause": [],
"onStop": [],
"onUpdate": [],
"onFinish": []
}
},
"http": {
"http": {
"user": null,
"pwd": null,
"messages": {
"onLoad": {
"url": "",
"enabled": false
},
"onStart": {
"url": "",
"enabled": false
},
"onUpdate": {
"url": "",
"enabled": false
},
"onPause": {
"url": "",
"enabled": false
},
"onStop": {
"url": "",
"enabled": false
},
"onFinish": {
"url": "",
"enabled": false
}
},
"enabled": true
}
}
}
+27
View File
@@ -0,0 +1,27 @@
import { test } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('http://localhost:4001/editor');
await page.getByRole('button', { name: 'Event...' }).click();
await page.getByRole('menuitem', { name: 'Delete all events' }).click();
await page.getByRole('button', { name: 'Upload showfile' }).click();
await page.getByLabel('Select file to upload').click();
await page.getByLabel('Select file to upload').setInputFiles('e2e/tests/fixtures/test-db.json');
await page.getByRole('button', { name: 'Upload' }).click();
await page.getByRole('button', { name: 'Close' }).click();
// asset test events
await page.getByPlaceholder('Start').first().click();
await page.getByText('First test event').click();
await page.getByTestId('delay-input').click();
await page
.locator('div')
.filter({ hasText: /^SED\+10minNew start: 10:10:00$/ })
.getByPlaceholder('Start')
.click();
await page.getByText('+10minNew start: 10:10:00').click();
await page.getByText('Second test event').click();
await page.getByText('Lunch').click();
await page.getByText('Third test event').click();
});
+183
View File
@@ -0,0 +1,183 @@
{
"rundown": [
{
"title": "First test event",
"subtitle": "",
"presenter": "",
"note": "",
"endAction": "continue",
"timerType": "count-down",
"timeStart": 32400000,
"timeEnd": 36000000,
"duration": 3600000,
"isPublic": true,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event",
"revision": 4,
"id": "aa42f"
},
{
"duration": 600000,
"type": "delay",
"revision": 0,
"id": "b1d5a"
},
{
"title": "Second test event",
"subtitle": "",
"presenter": "",
"note": "",
"endAction": "continue",
"timerType": "count-down",
"timeStart": 36000000,
"timeEnd": 39600000,
"duration": 3600000,
"isPublic": true,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event",
"revision": 2,
"id": "d71bc"
},
{
"title": "Lunch",
"type": "block",
"id": "91682"
},
{
"title": "Third test event",
"subtitle": "",
"presenter": "",
"note": "",
"endAction": "continue",
"timerType": "count-down",
"timeStart": 39600000,
"timeEnd": 720000,
"duration": 47520000,
"isPublic": true,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event",
"revision": 2,
"id": "da5b4"
}
],
"eventData": {
"title": "All about Carlos demo event",
"publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
"backstageUrl": "www.getontime.no",
"backstageInfo": "WiFi: demobackstage\nPassword: ontimeproject",
"endMessage": ""
},
"settings": {
"app": "ontime",
"version": 2,
"serverPort": 4001,
"lock": null,
"pinCode": "1234",
"timeFormat": "24"
},
"viewSettings": {
"overrideStyles": false
},
"aliases": [
{
"id": "0b0b3",
"enabled": true,
"alias": "test",
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
}
],
"userFields": {
"user0": "user0",
"user1": "user1",
"user2": "user2",
"user3": "user3",
"user4": "user4",
"user5": "user5",
"user6": "user6",
"user7": "user7",
"user8": "user8",
"user9": "user9"
},
"osc": {
"portIn": 8888,
"portOut": 9999,
"targetIP": "127.0.0.1",
"enabledIn": false,
"enabledOut": false,
"subscriptions": {
"onLoad": [],
"onStart": [],
"onPause": [],
"onStop": [],
"onUpdate": [],
"onFinish": []
}
},
"http": {
"http": {
"user": null,
"pwd": null,
"messages": {
"onLoad": {
"url": "",
"enabled": false
},
"onStart": {
"url": "",
"enabled": false
},
"onUpdate": {
"url": "",
"enabled": false
},
"onPause": {
"url": "",
"enabled": false
},
"onStop": {
"url": "",
"enabled": false
},
"onFinish": {
"url": "",
"enabled": false
}
},
"enabled": true
}
}
}
+25
View File
@@ -0,0 +1,25 @@
import fs from 'fs';
import path from 'path';
// haven't been able to get this to work
// it is well discussed in the issues, so should be able to find something
// https://playwrightsolutions.com/making-a-post/
// https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-fetch
// the upload is accepted by backend we receive 400 after upload and parse
export async function uploadTestDb(request) {
const filePath = path.resolve('e2e/tests/fixtures/test-db.json');
const file = fs.readFileSync(filePath);
const response = await request.post('http://localhost:4001/ontime/db?onlyRundown=false', {
multipart: {
file: {
fileName: filePath,
mimeType: "application/json",
buffer: file,
},
},
});
return response;
}
+4 -1
View File
@@ -1,3 +1,6 @@
{
"sourceType": "module"
"sourceType": "module",
"compilerOptions": {
"esModuleInterop": true
}
}
+2 -2
View File
@@ -30,12 +30,12 @@
"dist-win": "turbo run dist-win",
"dist-mac": "turbo run dist-mac",
"dist-linux": "turbo run dist-linux",
"test": "vitest",
"e2e": "cross-env DEBUG=pw:webserver npx playwright test -c playwright.config.ts",
"e2e:i": "npx playwright codegen",
"cleanup": "rm -rf node_modules && rm -rf **/node_modules && rm -rf **/**/node_modules"
},
"devDependencies": {
"@playwright/test": "^1.32.1",
"@playwright/test": "^1.32.3",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
+18 -18
View File
@@ -4,7 +4,7 @@ importers:
.:
specifiers:
'@playwright/test': ^1.32.1
'@playwright/test': ^1.32.3
'@types/node': ^18.11.18
'@typescript-eslint/eslint-plugin': ^5.48.0
'@typescript-eslint/parser': ^5.48.0
@@ -16,7 +16,7 @@ importers:
turbo: ^1.8.8
typescript: ^4.8.3
devDependencies:
'@playwright/test': 1.32.1
'@playwright/test': 1.32.3
'@types/node': 18.11.18
'@typescript-eslint/eslint-plugin': 5.48.0_k73wpmdolxikpyqun3p36akaaq
'@typescript-eslint/parser': 5.48.0_iukboom6ndih5an6iafl45j2fe
@@ -2273,7 +2273,7 @@ packages:
'@jest/schemas': 29.0.0
'@types/istanbul-lib-coverage': 2.0.4
'@types/istanbul-reports': 3.0.1
'@types/node': 18.11.18
'@types/node': 18.15.11
'@types/yargs': 17.0.19
chalk: 4.1.2
dev: true
@@ -2363,13 +2363,13 @@ packages:
fastq: 1.15.0
dev: true
/@playwright/test/1.32.1:
resolution: {integrity: sha512-FTwjCuhlm1qHUGf4hWjfr64UMJD/z0hXYbk+O387Ioe6WdyZQ+0TBDAc6P+pHjx2xCv1VYNgrKbYrNixFWy4Dg==}
/@playwright/test/1.32.3:
resolution: {integrity: sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==}
engines: {node: '>=14'}
hasBin: true
dependencies:
'@types/node': 18.11.18
playwright-core: 1.32.1
'@types/node': 18.15.11
playwright-core: 1.32.3
optionalDependencies:
fsevents: 2.3.2
dev: true
@@ -2826,7 +2826,7 @@ packages:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
dependencies:
'@types/connect': 3.4.35
'@types/node': 18.11.18
'@types/node': 18.15.11
dev: true
/@types/cacheable-request/6.0.3:
@@ -2834,7 +2834,7 @@ packages:
dependencies:
'@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.4
'@types/node': 16.18.23
'@types/node': 18.15.11
'@types/responselike': 1.0.0
dev: true
@@ -2867,7 +2867,7 @@ packages:
/@types/connect/3.4.35:
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
dependencies:
'@types/node': 18.11.18
'@types/node': 18.15.11
dev: true
/@types/debug/4.1.7:
@@ -2883,7 +2883,7 @@ packages:
/@types/express-serve-static-core/4.17.33:
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
dependencies:
'@types/node': 18.11.18
'@types/node': 18.15.11
'@types/qs': 6.9.7
'@types/range-parser': 1.2.4
dev: true
@@ -2946,7 +2946,7 @@ packages:
/@types/keyv/3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
'@types/node': 16.18.23
'@types/node': 18.15.11
dev: true
/@types/lodash.mergewith/4.6.7:
@@ -3043,7 +3043,7 @@ packages:
/@types/responselike/1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
'@types/node': 16.18.23
'@types/node': 18.15.11
dev: true
/@types/scheduler/0.16.2:
@@ -3057,7 +3057,7 @@ packages:
resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==}
dependencies:
'@types/mime': 3.0.1
'@types/node': 18.11.18
'@types/node': 18.15.11
dev: true
/@types/stack-utils/2.0.1:
@@ -3096,7 +3096,7 @@ packages:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
dependencies:
'@types/node': 16.18.23
'@types/node': 18.15.11
dev: true
optional: true
@@ -6160,7 +6160,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies:
'@jest/types': 29.3.1
'@types/node': 18.11.18
'@types/node': 18.15.11
chalk: 4.1.2
ci-info: 3.7.1
graceful-fs: 4.2.11
@@ -7005,8 +7005,8 @@ packages:
pathe: 1.1.0
dev: true
/playwright-core/1.32.1:
resolution: {integrity: sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==}
/playwright-core/1.32.3:
resolution: {integrity: sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==}
engines: {node: '>=14'}
hasBin: true
dev: true