dx: steadier tests
Massimo Melina committed
Jun 13, 2025 at 19:48 UTC
4c72f017130b1b38995bda08dd8ba2f1ae881d84
4 files changed
+96
-87
e2e/common.ts
new
+20
@@ -0,0 +1,20 @@
1
+import { test } from '@playwright/test'
2
+import fs from 'fs'
3
+
4
+export const username = 'rejetto'
5
+export const password = 'password'
6
+export const URL = 'http://localhost:81/'
7
+export const uploadName = 'uploaded'
8
+
9
+const t = Date.UTC(2025, 0, 20, 3, 0, 0, 0) / 1000 // a fixed timestamp, for visual comparison
10
+
11
+test.beforeAll(clearUploads)
12
+
13
+export function clearUploads() {
14
+ fs.unlink('tests/' + uploadName, () => {});
15
+ resetTimestamp()
16
+}
17
+
18
+export function resetTimestamp() {
19
+ fs.utimesSync('tests', t, t);
20
+}
\ No newline at end of file
e2e/frontend.spec.ts
+2
-86
@@ -1,25 +1,7 @@
1
import { test, expect, Page } from '@playwright/test'
2
import fs from 'fs'
3
import { wait } from '../src/cross'
4
-
5
-const username = 'rejetto'
6
-const password = 'password'
7
-const fileToUpload = 'dev-plugins.md'
8
-const uploadName = 'uploaded'
9
-const URL = 'http://localhost:81/'
10
-
11
-const t = Date.UTC(2025, 0, 20, 3, 0, 0, 0) / 1000 // a fixed timestamp, for visual comparison
12
-
13
-test.beforeAll(clearUploads)
14
-
15
-function clearUploads() {
16
- fs.unlink('tests/' + uploadName, () => {});
17
- resetTimestamp()
18
-}
19
-
20
-function resetTimestamp() {
21
- fs.utimesSync('tests', t, t);
22
-}
4
+import { password, resetTimestamp, URL, username } from './common'
5
6
// a generic test touch several parts
7
test('around1', async ({ page }) => {
@@ -133,51 +115,8 @@ test('around1', async ({ page }) => {
115
await expect(page.getByText('file, 10 folders, 6 B')).toBeVisible();
116
});
117
136
-test('upload1', async ({ page, context, browserName, browser }) => {
137
- if (page.viewportSize()?.width! < 1000 || browserName !== 'chromium') return // test only for desktop, as safari has no cdpSession, and to disconnect i need only 1 upload at a time
138
- await page.goto(URL);
139
- await page.getByRole('button', { name: 'Login' }).click();
140
- await page.getByRole('textbox', { name: 'Username' }).fill(username);
141
- await page.getByRole('textbox', { name: 'Username' }).press('Tab');
142
- await page.getByRole('textbox', { name: 'Password' }).fill(password);
143
- await page.getByRole('button', { name: 'Continue' }).click();
144
- await page.locator('div').filter({ hasText: 'Logged in' }).nth(3).click();
145
-
146
- await page.getByRole('link', { name: 'for-admins, Folder' }).click();
147
- await page.getByRole('link', { name: 'upload, Folder' }).click();
148
-
149
- await page.getByRole('button', { name: 'Options' }).click();
150
- const pageAdminPromise = page.waitForEvent('popup');
151
- await page.getByRole('button', { name: 'Admin-panel' }).click();
152
- const pageAdmin = await pageAdminPromise;
153
- await pageAdmin.goto(URL + '~/admin/#/monitoring'); // cross-device way of changing page
154
- await page.locator('div').filter({ hasText: 'xOptionsAdmin-panelSort by:' }).nth(2).click();
155
- await page.getByRole('button', { name: 'Close' }).click();
156
- await page.getByRole('button', { name: 'Upload' }).click();
157
- const fileChooserPromise = page.waitForEvent('filechooser');
158
- await page.getByRole('button', { name: 'Pick files' }).click();
159
- const fileChooser = await fileChooserPromise;
160
- await fileChooser.setFiles(fileToUpload);
161
- const cdpSession = await context.newCDPSession(page)
162
- await cdpSession.send('Network.emulateNetworkConditions', NETWORK_PRESETS.Regular2G)
163
- await page.getByRole('button', { name: 'Edit' }).click();
164
- await page.getByRole('textbox').fill(uploadName);
165
- await page.getByRole('button', { name: 'Continue' }).click();
166
- await page.getByRole('button', { name: 'Send 1 file' }).click();
167
- await wait(2000)
168
- await pageAdmin.getByRole('cell', { name: uploadName }).click();
169
- await pageAdmin.getByRole('button', { name: '(Disconnect)' }).click();
170
- await pageAdmin.getByRole('button', { name: '(Close)' }).click();
171
- await pageAdmin.close()
172
- await page.getByText('Copy links').click();
173
- await page.getByText('Operation successful').click();
174
- await page.getByRole('button', { name: 'Close' }).click();
175
- await cdpSession?.send('Network.emulateNetworkConditions', NETWORK_PRESETS.NoThrottle)
176
- clearUploads()
177
-});
178
-
118
test('search1', async ({ page }) => {
180
- fs.utimesSync('tests', t, t)
119
+ resetTimestamp()
120
await page.goto(URL);
121
await page.getByRole('button', { name: 'Search' }).click();
122
await page.locator('input[name="name"]').fill('a');
@@ -350,26 +289,3 @@ async function screenshot(page: Page, selectorForMask='') {
289
await wait(1000) // this accounts especially for our DataTable component which takes time to set the layout
290
return expect(page).toHaveScreenshot({ fullPage: true, mask: [page.locator(`.maskInTests${selectorForMask}`)] });
291
}
353
-
354
-const NETWORK_PRESETS = {
355
- Offline: {
356
- offline: true,
357
- downloadThroughput: 0,
358
- uploadThroughput: 0,
359
- latency: 0,
360
- connectionType: 'none',
361
- },
362
- NoThrottle: {
363
- offline: false,
364
- downloadThroughput: -1,
365
- uploadThroughput: -1,
366
- latency: 0,
367
- },
368
- Regular2G: {
369
- offline: false,
370
- downloadThroughput: (250 * 1024) / 8,
371
- uploadThroughput: (120 * 1024) / 8,
372
- latency: 300,
373
- connectionType: 'cellular2g',
374
- },
375
-} as const;
e2e/serial.spec.ts
new
+73
@@ -0,0 +1,73 @@
1
+import { test } from '@playwright/test'
2
+import { wait } from '../src/cross'
3
+import { clearUploads, password, uploadName, URL, username } from './common'
4
+
5
+// this test is separated to run serially, as it will modify folder timestamp for a few seconds, during which other tests may fail
6
+
7
+export const fileToUpload = 'dev-plugins.md'
8
+
9
+test('upload1', async ({ page, context, browserName }) => {
10
+ if (page.viewportSize()?.width! < 1000 || browserName !== 'chromium') return // test only for desktop, as safari has no cdpSession, and to disconnect i need only 1 upload at a time
11
+ await page.goto(URL);
12
+ await page.getByRole('button', { name: 'Login' }).click();
13
+ await page.getByRole('textbox', { name: 'Username' }).fill(username);
14
+ await page.getByRole('textbox', { name: 'Username' }).press('Tab');
15
+ await page.getByRole('textbox', { name: 'Password' }).fill(password);
16
+ await page.getByRole('button', { name: 'Continue' }).click();
17
+ await page.locator('div').filter({ hasText: 'Logged in' }).nth(3).click();
18
+
19
+ await page.getByRole('link', { name: 'for-admins, Folder' }).click();
20
+ await page.getByRole('link', { name: 'upload, Folder' }).click();
21
+
22
+ await page.getByRole('button', { name: 'Options' }).click();
23
+ const pageAdminPromise = page.waitForEvent('popup');
24
+ await page.getByRole('button', { name: 'Admin-panel' }).click();
25
+ const pageAdmin = await pageAdminPromise;
26
+ await pageAdmin.goto(URL + '~/admin/#/monitoring'); // cross-device way of changing page
27
+ await page.locator('div').filter({ hasText: 'xOptionsAdmin-panelSort by:' }).nth(2).click();
28
+ await page.getByRole('button', { name: 'Close' }).click();
29
+ await page.getByRole('button', { name: 'Upload' }).click();
30
+ const fileChooserPromise = page.waitForEvent('filechooser');
31
+ await page.getByRole('button', { name: 'Pick files' }).click();
32
+ const fileChooser = await fileChooserPromise;
33
+ await fileChooser.setFiles(fileToUpload);
34
+ const cdpSession = await context.newCDPSession(page)
35
+ await cdpSession.send('Network.emulateNetworkConditions', NETWORK_PRESETS.Regular2G)
36
+ await page.getByRole('button', { name: 'Edit' }).click();
37
+ await page.getByRole('textbox').fill(uploadName);
38
+ await page.getByRole('button', { name: 'Continue' }).click();
39
+ await page.getByRole('button', { name: 'Send 1 file' }).click();
40
+ await wait(2000)
41
+ await pageAdmin.getByRole('cell', { name: uploadName }).click();
42
+ await pageAdmin.getByRole('button', { name: '(Disconnect)' }).click();
43
+ await pageAdmin.getByRole('button', { name: '(Close)' }).click();
44
+ await pageAdmin.close()
45
+ await page.getByText('Copy links').click();
46
+ await page.getByText('Operation successful').click();
47
+ await page.getByRole('button', { name: 'Close' }).click();
48
+ await cdpSession?.send('Network.emulateNetworkConditions', NETWORK_PRESETS.NoThrottle)
49
+ clearUploads()
50
+});
51
+
52
+const NETWORK_PRESETS = {
53
+ Offline: {
54
+ offline: true,
55
+ downloadThroughput: 0,
56
+ uploadThroughput: 0,
57
+ latency: 0,
58
+ connectionType: 'none',
59
+ },
60
+ NoThrottle: {
61
+ offline: false,
62
+ downloadThroughput: -1,
63
+ uploadThroughput: -1,
64
+ latency: 0,
65
+ },
66
+ Regular2G: {
67
+ offline: false,
68
+ downloadThroughput: (250 * 1024) / 8,
69
+ uploadThroughput: (120 * 1024) / 8,
70
+ latency: 300,
71
+ connectionType: 'cellular2g',
72
+ },
73
+} as const;
package.json
+1
-1
@@ -22,7 +22,7 @@
22
"test-with-server": "rm -rf tests/work && (node dist/src --cwd tests/work --config tests & echo $! > .server_pid) && sleep 2 && node --import tsx --test tests/test.ts; _exit=$?; SERVER_PID=$(cat ./.server_pid); kill \"$SERVER_PID\" 2>/dev/null || true; rm -f ./.server_pid; exit $_exit",
23
"test-ui": "npx playwright test --ui",
24
"pub": "cd dist && npm publish",
25
- "dist": "npm run build-all && npx playwright test && npm run dist-bin",
25
+ "dist": "npm run build-all && npx playwright test frontend && npx playwright test serial && npm run dist-bin",
26
"dist-bin": "npm run dist-modules && npm run dist-bin-win && npm run dist-bin-linux && npm run dist-bin-mac && npm run dist-bin-mac-arm",
27
"dist-modules": "cp package*.json central.json dist && cd dist && npm ci --omit=dev && cd .. && node prune_modules",
28
"dist-pre": "cd dist && rm -rf node_modules/@node-rs/crc32-*",