···11import {devices, type PlaywrightTestConfig} from '@playwright/test';
2233-const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
33+const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3003';
4455/**
66 * @see https://playwright.dev/docs/test-configuration
···12121313 // you can adjust this value locally to match your machine's power,
1414 // or pass `--workers x` to playwright
1515- workers: process.env.CI ? 1 : 2,
1515+ workers: 1,
16161717 /* Maximum time one test can run for. */
1818 timeout: 30 * 1000,
···2222 * Maximum time expect() should wait for the condition to be met.
2323 * For example in `await expect(locator).toHaveText();`
2424 */
2525- timeout: 2000,
2525+ timeout: 3000,
2626 },
27272828 /* Fail the build on CI if you accidentally left test.only in the source code. */
···30303131 /* Retry on CI only */
3232 retries: process.env.CI ? 1 : 0,
3333+ /* fail fast */
3434+ maxFailures: process.env.CI ? 1 : 0,
33353436 /* Reporter to use. See https://playwright.dev/docs/test-reporters */
3537 reporter: process.env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
···4143 locale: 'en-US',
42444345 /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
4444- actionTimeout: 2000,
4646+ actionTimeout: 3000,
45474648 /* Maximum time allowed for navigation, such as `page.goto()`. */
4749 navigationTimeout: 10 * 1000,
···9597 },
9698 ],
97999898- /* Folder for test artifacts such as screenshots, videos, traces, etc. */
100100+ /* Folder for test artifacts created during test execution such as screenshots, traces, etc. */
99101 outputDir: 'tests/e2e/test-artifacts/',
100100- /* Folder for test artifacts such as screenshots, videos, traces, etc. */
102102+ /* Folder for explicit snapshots for visual testing */
101103 snapshotDir: 'tests/e2e/test-snapshots/',
102104} satisfies PlaywrightTestConfig;
+88
tests/e2e/README.md
···175175If you know noteworthy tests that can act as an inspiration for new tests,
176176please add some details here.
177177178178+### Understanding and waiting for page loads
179179+180180+[Waiting for a load state](https://playwright.dev/docs/api/class-frame#frame-wait-for-load-state)
181181+sound like a convenient way to ensure the page was loaded,
182182+but it only works once and consecutive calls to it
183183+(e.g. after clicking a button which should reload a page)
184184+return immediately without waiting for *another* load event.
185185+186186+If you match something which is on both the old and the new page,
187187+you might succeed before the page was reloaded,
188188+although the code using a `waitForLoadState` might intuitively suggest
189189+the page was changed before.
190190+191191+Interacting with the page before the reload
192192+(e.g. by opening a dropdown)
193193+might then race and result in flaky tests,
194194+depending on the speed of the hardware running the test.
195195+196196+A possible way to test that an interaction worked is by checking for a known change first.
197197+For example:
198198+199199+- you submit a form and you want to check that the content persisted
200200+- checking for the content directly would succeed even without a page reload
201201+- check for a success message first (will wait until it appears), then verify the content
202202+203203+Alternatively, if you know the backend request that will be made before the reload,
204204+you can explicitly wait for it:
205205+206206+~~~js
207207+const submitted = page.waitForResponse('/my/backend/post/request');
208208+await page.locator('button').first().click(); // perform your interaction
209209+await submitted;
210210+~~~
211211+212212+If the page redirects to another URL,
213213+you can alternatively use:
214214+215215+~~~js
216216+await page.waitForURL('**/target.html');
217217+~~~
218218+219219+### Only sign in if necessary
220220+221221+Signing in takes time and is actually executed step-by-step.
222222+If your test does not rely on a user account, skip this step.
223223+224224+~~~js
225225+test('For anyone', async ({page}) => {
226226+ await page.goto('/somepage');
227227+~~~
228228+229229+If you need a user account, you can use something like:
230230+231231+~~~js
232232+import {test, login_user, login} from './utils_e2e.ts';
233233+234234+test.beforeAll(async ({browser}, workerInfo) => {
235235+ await login_user(browser, workerInfo, 'user2'); // or another user
236236+});
237237+238238+test('For signed users only', async ({browser}, workerInfo) => {
239239+ const page = await login({browser}, workerInfo);
240240+~~~
241241+178242### Run tests very selectively
179243180244Browser testing can take some time.
···264328265329The patterns are evaluated on a "first-match" basis.
266330Under the hood, [gobwas/glob](https://github.com/gobwas/glob) is used.
331331+332332+## Grouped retry for interactions
333333+334334+Sometimes, it can be necessary to retry certain interactions together.
335335+Consider the following procedure:
336336+337337+1. click to open a dropdown
338338+2. interact with content in the dropdown
339339+340340+When for some reason the dropdown does not open,
341341+for example because of it taking time to initialize after page load,
342342+the click will succeed,
343343+but the depending interaction won't,
344344+although playwright repeatedly tries to find the content.
345345+346346+You can [group statements using toPass]()https://playwright.dev/docs/test-assertions#expecttopass).
347347+This code retries the dropdown click until the second item is found.
348348+349349+~~~js
350350+await expect(async () => {
351351+ await page.locator('.dropdown').click();
352352+ await page.locator('.dropdown .item').first().click();
353353+}).toPass();
354354+~~~
···1313 const page = await context.newPage();
14141515 await page.goto('/user1');
1616- await page.waitForLoadState('networkidle');
17161817 // Check if following and then unfollowing works.
1918 // This checks that the event listeners of
···99test(`Search for long titles and test for no overflow`, async ({page}, workerInfo) => {
1010 test.skip(workerInfo.project.name === 'Mobile Safari', 'Fails as always, see https://codeberg.org/forgejo/forgejo/pulls/5326#issuecomment-2313275');
1111 await page.goto('/user2/repo1/wiki');
1212- await page.waitForLoadState('networkidle');
1312 await page.getByPlaceholder('Search wiki').fill('spaces');
1413 await page.getByPlaceholder('Search wiki').click();
1514 // workaround: HTMX listens on keyup events, playwright's fill only triggers the input event