better code: centralized
Massimo Melina committed
Apr 21, 2026 at 11:18 UTC
69ddf06bd2b4cfc55b29af82d7d5b495db37ee54
4 files changed
+20
-18
e2e/admin-vfs.spec.ts
+8
-8
@@ -1,5 +1,5 @@
1
import { expect, Page, test } from '@playwright/test'
2
-import { clickAdminMenu, URL, username, password, clickIconBtn } from './common'
2
+import { ADMIN_URL, clickAdminMenu, username, password, clickIconBtn } from './common'
3
4
async function selectVfsNode(page: Page, name: string, expectedId: string) {
5
await page.getByRole('treeitem', { name, exact: true }).click()
@@ -23,7 +23,7 @@ async function expandVfsNode(page: Page, nodeId: string) {
23
}
24
25
test('move via cut/paste keeps node visible', async ({ page }) => {
26
- await page.goto(URL + '~/admin/')
26
+ await page.goto(ADMIN_URL)
27
await page.getByRole('textbox', { name: 'Username' }).fill(username)
28
await page.getByRole('textbox', { name: 'Password' }).fill(password)
29
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -61,7 +61,7 @@ test('move via cut/paste keeps node visible', async ({ page }) => {
61
})
62
63
test('move to nested destination expands ancestors', async ({ page }) => {
64
- await page.goto(URL + '~/admin/')
64
+ await page.goto(ADMIN_URL)
65
await page.getByRole('textbox', { name: 'Username' }).fill(username)
66
await page.getByRole('textbox', { name: 'Password' }).fill(password)
67
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -103,7 +103,7 @@ test('move to nested destination expands ancestors', async ({ page }) => {
103
})
104
105
test('move into empty folder keeps node visible', async ({ page }) => {
106
- await page.goto(URL + '~/admin/')
106
+ await page.goto(ADMIN_URL)
107
await page.getByRole('textbox', { name: 'Username' }).fill(username)
108
await page.getByRole('textbox', { name: 'Password' }).fill(password)
109
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -141,7 +141,7 @@ test('move into empty folder keeps node visible', async ({ page }) => {
141
})
142
143
test('delete virtual folder updates tree and marks modified', async ({ page }) => {
144
- await page.goto(URL + '~/admin/')
144
+ await page.goto(ADMIN_URL)
145
await page.getByRole('textbox', { name: 'Username' }).fill(username)
146
await page.getByRole('textbox', { name: 'Password' }).fill(password)
147
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -165,7 +165,7 @@ test('delete virtual folder updates tree and marks modified', async ({ page }) =
165
})
166
167
test('undo toggles with single-level redo behavior', async ({ page }) => {
168
- await page.goto(URL + '~/admin/')
168
+ await page.goto(ADMIN_URL)
169
await page.getByRole('textbox', { name: 'Username' }).fill(username)
170
await page.getByRole('textbox', { name: 'Password' }).fill(password)
171
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -217,7 +217,7 @@ test('undo toggles with single-level redo behavior', async ({ page }) => {
217
})
218
219
test('apply keeps unset permissions nullish in-memory', async ({ page }) => {
220
- await page.goto(URL + '~/admin/')
220
+ await page.goto(ADMIN_URL)
221
await page.getByRole('textbox', { name: 'Username' }).fill(username)
222
await page.getByRole('textbox', { name: 'Password' }).fill(password)
223
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
@@ -248,7 +248,7 @@ test('apply keeps unset permissions nullish in-memory', async ({ page }) => {
248
})
249
250
test('apply refreshes inherited permissions for descendants in-memory', async ({ page }) => {
251
- await page.goto(URL + '~/admin/')
251
+ await page.goto(ADMIN_URL)
252
await page.getByRole('textbox', { name: 'Username' }).fill(username)
253
await page.getByRole('textbox', { name: 'Password' }).fill(password)
254
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
e2e/common.ts
+4
-2
@@ -3,12 +3,14 @@ import fs from 'fs'
3
import { readFileSync } from 'node:fs'
4
import { resolve } from 'node:path'
5
import yaml from 'yaml'
6
+import { ADMIN_URI } from '../src/cross-const'
7
8
export const username = 'rejetto'
9
export const password = 'password'
10
// keep e2e URL aligned with the same config file used by tests/test.ts and server-for-test
11
const TEST_PORT = Number(yaml.parse(readFileSync(resolve(process.cwd(), 'tests/config.yaml'), 'utf8')).port)
11
-export const URL = `http://[::1]:${TEST_PORT}/`
12
+export const FRONTEND_URL = `http://[::1]:${TEST_PORT}/`
13
+export const ADMIN_URL = new URL(ADMIN_URI, FRONTEND_URL).href
14
export const uploadName = 'uploaded'
15
16
const t = Date.UTC(2025, 0, 20, 3, 0, 0, 0) / 1000 // a fixed timestamp, for visual comparison
@@ -52,7 +54,7 @@ export function clickIconBtn(title: string | RegExp, page: Page) {
54
}
55
56
export async function loginAdmin(page: Page) {
55
- await page.goto(URL + '~/admin/')
57
+ await page.goto(ADMIN_URL)
58
await page.getByRole('textbox', { name: 'Username' }).fill(username)
59
await page.getByRole('textbox', { name: 'Password' }).fill(password)
60
await page.getByRole('textbox', { name: 'Password' }).press('Enter')
e2e/frontend.spec.ts
+5
-5
@@ -2,14 +2,14 @@ import { test, expect, Page } from '@playwright/test'
2
import fs from 'fs'
3
import { wait } from '../src/cross'
4
import {
5
- clickAdminMenu, clickIconBtn, forwardConsole, loginAdmin, password, resetTimestamp, URL, username
5
+ clickAdminMenu, clickIconBtn, forwardConsole, loginAdmin, password, resetTimestamp, FRONTEND_URL, username
6
} from './common'
7
8
// a generic test touch several parts
9
test('around1', async ({ page }) => {
10
forwardConsole(page)
11
resetTimestamp()
12
- await page.goto(URL)
12
+ await page.goto(FRONTEND_URL)
13
await expect(page).toHaveTitle(/File server/)
14
await screenshot(page)
15
await page.getByRole('button', { name: 'Login' }).click()
@@ -119,7 +119,7 @@ test('around1', async ({ page }) => {
119
120
test('search1', async ({ page }) => {
121
resetTimestamp()
122
- await page.goto(URL)
122
+ await page.goto(FRONTEND_URL)
123
await page.getByRole('button', { name: 'Search' }).click()
124
await page.locator('input[name="name"]').fill('a')
125
await page.getByRole('button', { name: 'Continue' }).click()
@@ -175,7 +175,7 @@ test('search1', async ({ page }) => {
175
})
176
177
test('frontend-admin', async ({ page }) => {
178
- await page.goto(URL, { waitUntil: 'networkidle' })
178
+ await page.goto(FRONTEND_URL, { waitUntil: 'networkidle' })
179
await page.evaluate(() => document.fonts.ready) // aspetta i font
180
await page.getByRole('button', { name: 'Options' }).click()
181
// no admin button yet,
@@ -336,7 +336,7 @@ test('anew', async ({ page, browserName }) => {
336
})
337
338
test('order field', async ({ page }) => {
339
- await page.goto(URL + 'renameChild/orderTest/')
339
+ await page.goto(FRONTEND_URL + 'renameChild/orderTest/')
340
await expect(page.getByText('orderTest')).toBeVisible()
341
await expect(page.locator('.entry-name')).toHaveText(['B', 'A', 'C'])
342
})
e2e/serial.spec.ts
+3
-3
@@ -1,5 +1,5 @@
1
import { expect, test } from '@playwright/test'
2
-import { clearUploads, clickAdminMenu, clickIconBtn, loginAdmin, password, uploadName, URL, username } from './common'
2
+import { ADMIN_URL, clearUploads, clickAdminMenu, clickIconBtn, loginAdmin, password, uploadName, FRONTEND_URL, username } from './common'
3
4
// this test is separated to run serially, as it will modify folder timestamp for a few seconds, during which other tests may fail
5
test.describe.configure({ mode: 'serial' }) // to disconnect the upload consistently, i need only 1 upload at a time
@@ -12,7 +12,7 @@ export const fileToUpload = {
12
13
test('upload1', async ({ page, context, browserName }) => {
14
if (browserName !== 'chromium') return // only chromium has cdpSession
15
- await page.goto(URL);
15
+ await page.goto(FRONTEND_URL);
16
await page.getByRole('button', { name: 'Login' }).click();
17
await page.getByRole('textbox', { name: 'Username' }).fill(username);
18
await page.getByRole('textbox', { name: 'Password' }).fill(password);
@@ -26,7 +26,7 @@ test('upload1', async ({ page, context, browserName }) => {
26
const pageAdminPromise = page.waitForEvent('popup');
27
await page.getByRole('button', { name: 'Admin-panel' }).click();
28
const pageAdmin = await pageAdminPromise;
29
- await pageAdmin.goto(URL + '~/admin/#/monitoring'); // cross-device way of changing page
29
+ await pageAdmin.goto(ADMIN_URL + '#/monitoring'); // cross-device way of changing page
30
await page.locator('div').filter({ hasText: 'xOptionsAdmin-panelSort by:' }).nth(2).click();
31
await page.getByRole('button', { name: 'Close' }).click();
32
await page.getByRole('button', { name: 'Upload' }).click();