test: frontend upload
Massimo Melina committed
Jun 9, 2025 at 22:14 UTC
a1c99597ca01a712d38f957721ab2ed890297bf4
3 files changed
+89
-10
e2e/frontend.spec.ts
+87
-8
@@ -4,13 +4,27 @@ 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
+}
23
+
24
// a generic test touch several parts
25
test('around1', async ({ page }) => {
12
- fs.utimesSync('tests', t, t);
13
- await page.goto('http://localhost:81/');
26
+ resetTimestamp();
27
+ await page.goto(URL);
28
await expect(page).toHaveTitle(/File server/);
29
await screenshot(page);
30
await page.getByRole('button', { name: 'Login' }).click();
@@ -39,8 +53,8 @@ test('around1', async ({ page }) => {
53
await page.getByRole('checkbox', { name: 'alfa.txt' }).check();
54
await expect(page.getByRole('button', { name: 'Delete' })).toBeEnabled();
55
await page.getByRole('button', { name: 'Select' }).click();
42
- await page.getByRole('link', { name: 'home' }).click();
56
57
+ await page.getByRole('link', { name: 'home' }).click();
58
await page.getByRole('button', { name: username }).click();
59
await page.getByRole('button', { name: 'Logout' }).click();
60
await page.getByText('Logged out').click();
@@ -119,9 +133,52 @@ test('around1', async ({ page }) => {
133
await expect(page.getByText('file, 10 folders, 6 B')).toBeVisible();
134
});
135
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
+
179
test('search1', async ({ page }) => {
180
fs.utimesSync('tests', t, t)
124
- await page.goto('http://localhost:81/');
181
+ await page.goto(URL);
182
await page.getByRole('button', { name: 'Search' }).click();
183
await page.locator('input[name="name"]').fill('a');
184
await page.getByRole('button', { name: 'Continue' }).click();
@@ -177,7 +234,7 @@ test('search1', async ({ page }) => {
234
});
235
236
test('frontend-admin', async ({ page }) => {
180
- await page.goto('http://localhost:81/');
237
+ await page.goto(URL);
238
await page.getByRole('button', { name: 'Options' }).click();
239
// no admin button yet,
240
await expect(page.getByRole('dialog')).toMatchAriaSnapshot(`
@@ -217,7 +274,7 @@ test('frontend-admin', async ({ page }) => {
274
275
test('admin1', async ({ page }) => {
276
await fs.promises.rm('tests/work/logs', {force: true, recursive: true}); // clear logs to have consistent screenshots
220
- await page.goto('http://localhost:81/~/admin/');
277
+ await page.goto(URL + '~/admin/');
278
await page.getByRole('textbox', { name: 'Username' }).fill(username);
279
await page.getByRole('textbox', { name: 'Password' }).fill(password);
280
await page.getByRole('textbox', { name: 'Password' }).press('Enter');
@@ -237,7 +294,6 @@ test('admin1', async ({ page }) => {
294
function dataTableLoading() {
295
return expect(page.getByRole('grid').getByRole('img')).toBeVisible({ visible: false });
296
}
240
-// const dataTableContent = '.MuiDataGrid-overlayWrapperInner,.MuiDataGrid-virtualScroller'
297
298
await clickMenu('Internet'); // initiate the get_nat process, so we'll have to wait less, later
299
await clickMenu('Shared files')
@@ -293,4 +349,27 @@ async function screenshot(page: Page, selectorForMask='') {
349
selectorForMask = ',' + selectorForMask
350
await wait(1000) // this accounts especially for our DataTable component which takes time to set the layout
351
return expect(page).toHaveScreenshot({ fullPage: true, mask: [page.locator(`.maskInTests${selectorForMask}`)] });
296
-}
\ No newline at end of file
352
+}
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;
playwright.config.ts
+1
-1
@@ -13,7 +13,7 @@ import { defineConfig, devices } from '@playwright/test';
13
*/
14
export default defineConfig({
15
testDir: './e2e',
16
- timeout: 20_000,
16
+ timeout: 25_000,
17
fullyParallel: true, // Run tests in files in parallel
18
forbidOnly: !!process.env.CI, // Fail the build on CI if you accidentally left test.only in the source code.
19
retries: process.env.CI ? 2 : 0, // Retry on CI only
tests/config.yaml
+1
-1
@@ -4,7 +4,7 @@ allowed_referer: x.com
4
localhost_admin: false
5
vfs:
6
masks:
7
- "**/config.yaml*|test.ts|work":
7
+ "**/config.yaml*|test.ts|work|*uploaded":
8
can_see: false
9
tests/page/!*.png:
10
mime: text/plain