mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-01-10 15:42:16 +01:00
chore(e2e): simplify authentication setup (#6400)
Some checks are pending
/ release (push) Waiting to run
testing / test-remote-cacher (redis) (push) Blocked by required conditions
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-e2e (push) Blocked by required conditions
testing / test-remote-cacher (valkey) (push) Blocked by required conditions
testing / test-remote-cacher (garnet) (push) Blocked by required conditions
testing / test-remote-cacher (redict) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
Some checks are pending
/ release (push) Waiting to run
testing / test-remote-cacher (redis) (push) Blocked by required conditions
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-e2e (push) Blocked by required conditions
testing / test-remote-cacher (valkey) (push) Blocked by required conditions
testing / test-remote-cacher (garnet) (push) Blocked by required conditions
testing / test-remote-cacher (redict) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
Replaced manual login and context loading across tests with Playwright's `test.use` configuration for user authentication. This simplifies test setup, improves readability, and reduces repetition. For #6362 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6400 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: Julian Schlarb <julian.schlarb@denktmit.de> Co-committed-by: Julian Schlarb <julian.schlarb@denktmit.de>
This commit is contained in:
parent
a2eb249766
commit
68d690b6b9
19 changed files with 327 additions and 254 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -73,6 +73,7 @@ cpu.out
|
||||||
/tests/e2e/reports
|
/tests/e2e/reports
|
||||||
/tests/e2e/test-artifacts
|
/tests/e2e/test-artifacts
|
||||||
/tests/e2e/test-snapshots
|
/tests/e2e/test-snapshots
|
||||||
|
/tests/e2e/.auth
|
||||||
/tests/*.ini
|
/tests/*.ini
|
||||||
/tests/**/*.git/**/*.sample
|
/tests/**/*.git/**/*.sample
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
|
@ -250,16 +250,18 @@ test('For anyone', async ({page}) => {
|
||||||
If you need a user account, you can use something like:
|
If you need a user account, you can use something like:
|
||||||
|
|
||||||
~~~js
|
~~~js
|
||||||
import {test, login_user, login} from './utils_e2e.ts';
|
import {test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
// reuse user2 token from scope `shared`
|
||||||
await login_user(browser, workerInfo, 'user2'); // or another user
|
test.use({user: 'user2', authScope: 'shared'})
|
||||||
});
|
|
||||||
|
|
||||||
test('For signed users only', async ({browser}, workerInfo) => {
|
test('For signed users only', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
|
})
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
users are created in [utils_e2e_test.go](utils_e2e_test.go)
|
||||||
|
|
||||||
### Run tests very selectively
|
### Run tests very selectively
|
||||||
|
|
||||||
Browser testing can take some time.
|
Browser testing can take some time.
|
||||||
|
|
|
@ -10,18 +10,13 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
const workflow_trigger_notification_text = 'This workflow has a workflow_dispatch event trigger.';
|
const workflow_trigger_notification_text = 'This workflow has a workflow_dispatch event trigger.';
|
||||||
|
test.describe('Workflow Authenticated user2', () => {
|
||||||
|
test.use({user: 'user2'});
|
||||||
|
|
||||||
test('workflow dispatch present', async ({browser}, workerInfo) => {
|
test('workflow dispatch present', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||||
|
|
||||||
await expect(page.getByText(workflow_trigger_notification_text)).toBeVisible();
|
await expect(page.getByText(workflow_trigger_notification_text)).toBeVisible();
|
||||||
|
@ -34,13 +29,10 @@ test('workflow dispatch present', async ({browser}, workerInfo) => {
|
||||||
await run_workflow_btn.click();
|
await run_workflow_btn.click();
|
||||||
await expect(menu).toBeVisible();
|
await expect(menu).toBeVisible();
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('workflow dispatch error: missing inputs', async ({browser}, workerInfo) => {
|
test('dispatch error: missing inputs', async ({page}, testInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
|
test.skip(testInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
|
||||||
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||||
|
|
||||||
|
@ -56,14 +48,10 @@ test('workflow dispatch error: missing inputs', async ({browser}, workerInfo) =>
|
||||||
|
|
||||||
await expect(page.getByText('Require value for input "String w/o. default".')).toBeVisible();
|
await expect(page.getByText('Require value for input "String w/o. default".')).toBeVisible();
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('workflow dispatch success', async ({browser}, workerInfo) => {
|
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
|
|
||||||
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
|
test('dispatch success', async ({page}, testInfo) => {
|
||||||
|
test.skip(testInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari; see https://codeberg.org/forgejo/forgejo/pulls/3334#issuecomment-2033383');
|
||||||
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||||
|
|
||||||
await page.locator('#workflow_dispatch_dropdown>button').click();
|
await page.locator('#workflow_dispatch_dropdown>button').click();
|
||||||
|
@ -76,6 +64,7 @@ test('workflow dispatch success', async ({browser}, workerInfo) => {
|
||||||
|
|
||||||
await expect(page.locator('.run-list>:first-child .run-list-meta', {hasText: 'now'})).toBeVisible();
|
await expect(page.locator('.run-list>:first-child .run-list-meta', {hasText: 'now'})).toBeVisible();
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('workflow dispatch box not available for unauthenticated users', async ({page}) => {
|
test('workflow dispatch box not available for unauthenticated users', async ({page}) => {
|
||||||
|
|
|
@ -3,21 +3,24 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Correct link and tooltip', async ({browser}, workerInfo) => {
|
test.describe.configure({retries: 2});
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
test('Correct link and tooltip', async ({page}, testInfo) => {
|
||||||
|
if (testInfo.retry) {
|
||||||
|
await page.goto('/user2/test_workflows/actions');
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchResponse = page.waitForResponse((resp) => resp.url().includes('/repo/search?') && resp.status() === 200);
|
||||||
const response = await page.goto('/?repo-search-query=test_workflows');
|
const response = await page.goto('/?repo-search-query=test_workflows');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
await searchResponse;
|
||||||
|
|
||||||
const repoStatus = page.locator('.dashboard-repos .repo-owner-name-list > li:nth-child(1) > a:nth-child(2)');
|
const repoStatus = page.locator('.dashboard-repos .repo-owner-name-list > li:nth-child(1) > a:nth-child(2)');
|
||||||
// wait for network activity to cease (so status was loaded in frontend)
|
|
||||||
await page.waitForLoadState('networkidle'); // eslint-disable-line playwright/no-networkidle
|
|
||||||
await expect(repoStatus).toHaveAttribute('href', '/user2/test_workflows/actions', {timeout: 10000});
|
await expect(repoStatus).toHaveAttribute('href', '/user2/test_workflows/actions', {timeout: 10000});
|
||||||
await expect(repoStatus).toHaveAttribute('data-tooltip-content', /^(Error|Failure)$/);
|
await expect(repoStatus).toHaveAttribute('data-tooltip-content', /^(Error|Failure)$/);
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
import {test, expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Change git note', async ({browser}, workerInfo) => {
|
test('Change git note', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d');
|
let response = await page.goto('/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,11 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, login} from './utils_e2e.ts';
|
import {test, save_visual} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Menu accessibility', async ({browser}, workerInfo) => {
|
test('Menu accessibility', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/user2/repo1/issues/1');
|
await page.goto('/user2/repo1/issues/1');
|
||||||
await expect(page.getByLabel('user2 reacted eyes. Remove eyes')).toBeVisible();
|
await expect(page.getByLabel('user2 reacted eyes. Remove eyes')).toBeVisible();
|
||||||
await expect(page.getByLabel('reacted laugh. Remove laugh')).toBeVisible();
|
await expect(page.getByLabel('reacted laugh. Remove laugh')).toBeVisible();
|
||||||
|
@ -24,9 +21,8 @@ test('Menu accessibility', async ({browser}, workerInfo) => {
|
||||||
await expect(page.getByLabel('user1, user2 reacted laugh. Remove laugh')).toBeVisible();
|
await expect(page.getByLabel('user1, user2 reacted laugh. Remove laugh')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Hyperlink paste behaviour', async ({browser}, workerInfo) => {
|
test('Hyperlink paste behaviour', async ({page}, workerInfo) => {
|
||||||
test.skip(['Mobile Safari', 'Mobile Chrome', 'webkit'].includes(workerInfo.project.name), 'Mobile clients seem to have very weird behaviour with this test, which I cannot confirm with real usage');
|
test.skip(['Mobile Safari', 'Mobile Chrome', 'webkit'].includes(workerInfo.project.name), 'Mobile clients seem to have very weird behaviour with this test, which I cannot confirm with real usage');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/user2/repo1/issues/new');
|
await page.goto('/user2/repo1/issues/new');
|
||||||
await page.locator('textarea').click();
|
await page.locator('textarea').click();
|
||||||
// same URL
|
// same URL
|
||||||
|
@ -58,8 +54,7 @@ test('Hyperlink paste behaviour', async ({browser}, workerInfo) => {
|
||||||
await page.locator('textarea').fill('');
|
await page.locator('textarea').fill('');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Always focus edit tab first on edit', async ({browser}, workerInfo) => {
|
test('Always focus edit tab first on edit', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/repo1/issues/1');
|
const response = await page.goto('/user2/repo1/issues/1');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -82,9 +77,8 @@ test('Always focus edit tab first on edit', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Quote reply', async ({browser}, workerInfo) => {
|
test('Quote reply', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name !== 'firefox', 'Uses Firefox specific selection quirks');
|
test.skip(workerInfo.project.name !== 'firefox', 'Uses Firefox specific selection quirks');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/repo1/issues/1');
|
const response = await page.goto('/user2/repo1/issues/1');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -157,9 +151,8 @@ test('Quote reply', async ({browser}, workerInfo) => {
|
||||||
await editorTextarea.fill('');
|
await editorTextarea.fill('');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Pull quote reply', async ({browser}, workerInfo) => {
|
test('Pull quote reply', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name !== 'firefox', 'Uses Firefox specific selection quirks');
|
test.skip(workerInfo.project.name !== 'firefox', 'Uses Firefox specific selection quirks');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/commitsonpr/pulls/1/files');
|
const response = await page.goto('/user2/commitsonpr/pulls/1/files');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
/* eslint playwright/expect-expect: ["error", { "assertFunctionNames": ["check_wip"] }] */
|
/* eslint playwright/expect-expect: ["error", { "assertFunctionNames": ["check_wip"] }] */
|
||||||
|
|
||||||
import {expect, type Page} from '@playwright/test';
|
import {expect, type Page} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, login} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('Pull: Toggle WIP', () => {
|
test.describe('Pull: Toggle WIP', () => {
|
||||||
const prTitle = 'pull5';
|
const prTitle = 'pull5';
|
||||||
|
|
||||||
async function toggle_wip_to({page}, should: boolean) {
|
async function toggle_wip_to({page}, should: boolean) {
|
||||||
await page.waitForLoadState('domcontentloaded');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
if (should) {
|
if (should) {
|
||||||
|
@ -39,8 +38,7 @@ test.describe('Pull: Toggle WIP', () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test.beforeEach(async ({browser}, workerInfo) => {
|
test.beforeEach(async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/repo1/pulls/5');
|
const response = await page.goto('/user2/repo1/pulls/5');
|
||||||
expect(response?.status()).toBe(200); // Status OK
|
expect(response?.status()).toBe(200); // Status OK
|
||||||
// ensure original title
|
// ensure original title
|
||||||
|
@ -50,9 +48,8 @@ test.describe('Pull: Toggle WIP', () => {
|
||||||
await check_wip({page}, false);
|
await check_wip({page}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('simple toggle', async ({browser}, workerInfo) => {
|
test('simple toggle', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/user2/repo1/pulls/5');
|
await page.goto('/user2/repo1/pulls/5');
|
||||||
// toggle to WIP
|
// toggle to WIP
|
||||||
await toggle_wip_to({page}, true);
|
await toggle_wip_to({page}, true);
|
||||||
|
@ -62,9 +59,8 @@ test.describe('Pull: Toggle WIP', () => {
|
||||||
await check_wip({page}, false);
|
await check_wip({page}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('manual edit', async ({browser}, workerInfo) => {
|
test('manual edit', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/user2/repo1/pulls/5');
|
await page.goto('/user2/repo1/pulls/5');
|
||||||
// manually edit title to another prefix
|
// manually edit title to another prefix
|
||||||
await page.locator('#issue-title-edit-show').click();
|
await page.locator('#issue-title-edit-show').click();
|
||||||
|
@ -76,9 +72,8 @@ test.describe('Pull: Toggle WIP', () => {
|
||||||
await check_wip({page}, false);
|
await check_wip({page}, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('maximum title length', async ({browser}, workerInfo) => {
|
test('maximum title length', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/user2/repo1/pulls/5');
|
await page.goto('/user2/repo1/pulls/5');
|
||||||
// check maximum title length is handled gracefully
|
// check maximum title length is handled gracefully
|
||||||
const maxLenStr = prTitle + 'a'.repeat(240);
|
const maxLenStr = prTitle + 'a'.repeat(240);
|
||||||
|
@ -96,17 +91,16 @@ test.describe('Pull: Toggle WIP', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Issue: Labels', async ({browser}, workerInfo) => {
|
test('Issue: Labels', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
|
|
||||||
async function submitLabels({page}: {page: Page}) {
|
async function submitLabels({page}: { page: Page }) {
|
||||||
const submitted = page.waitForResponse('/user2/repo1/issues/labels');
|
const submitted = page.waitForResponse('/user2/repo1/issues/labels');
|
||||||
await page.locator('textarea').first().click(); // close via unrelated element
|
await page.locator('textarea').first().click(); // close via unrelated element
|
||||||
await submitted;
|
await submitted;
|
||||||
await page.waitForLoadState();
|
await page.waitForLoadState();
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
// select label list in sidebar only
|
// select label list in sidebar only
|
||||||
const labelList = page.locator('.issue-content-right .labels-list a');
|
const labelList = page.locator('.issue-content-right .labels-list a');
|
||||||
const response = await page.goto('/user2/repo1/issues/1');
|
const response = await page.goto('/user2/repo1/issues/1');
|
||||||
|
@ -144,9 +138,8 @@ test('Issue: Labels', async ({browser}, workerInfo) => {
|
||||||
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
|
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Issue: Assignees', async ({browser}, workerInfo) => {
|
test('Issue: Assignees', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
// select label list in sidebar only
|
// select label list in sidebar only
|
||||||
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a');
|
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a');
|
||||||
|
|
||||||
|
@ -182,9 +175,8 @@ test('Issue: Assignees', async ({browser}, workerInfo) => {
|
||||||
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
|
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New Issue: Assignees', async ({browser}, workerInfo) => {
|
test('New Issue: Assignees', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
// select label list in sidebar only
|
// select label list in sidebar only
|
||||||
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item');
|
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item');
|
||||||
|
|
||||||
|
@ -224,9 +216,8 @@ test('New Issue: Assignees', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Issue: Milestone', async ({browser}, workerInfo) => {
|
test('Issue: Milestone', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
|
|
||||||
const response = await page.goto('/user2/repo1/issues/1');
|
const response = await page.goto('/user2/repo1/issues/1');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
@ -248,9 +239,8 @@ test('Issue: Milestone', async ({browser}, workerInfo) => {
|
||||||
await expect(page.locator('.timeline-item.event').last()).toContainText('user2 removed this from the milestone1 milestone');
|
await expect(page.locator('.timeline-item.event').last()).toContainText('user2 removed this from the milestone1 milestone');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New Issue: Milestone', async ({browser}, workerInfo) => {
|
test('New Issue: Milestone', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
|
|
||||||
const response = await page.goto('/user2/repo1/issues/new');
|
const response = await page.goto('/user2/repo1/issues/new');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
|
@ -5,21 +5,16 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, load_logged_in_context, login_user} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Markdown image preview behaviour', async ({browser}, workerInfo) => {
|
test('Markdown image preview behaviour', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky behaviour on mobile safari;');
|
||||||
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
|
|
||||||
// Editing the root README.md file for image preview
|
// Editing the root README.md file for image preview
|
||||||
const editPath = '/user2/repo1/src/branch/master/README.md';
|
const editPath = '/user2/repo1/src/branch/master/README.md';
|
||||||
|
|
||||||
const page = await context.newPage();
|
|
||||||
const response = await page.goto(editPath, {waitUntil: 'domcontentloaded'});
|
const response = await page.goto(editPath, {waitUntil: 'domcontentloaded'});
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -43,12 +38,9 @@ test('Markdown image preview behaviour', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('markdown indentation', async ({browser}, workerInfo) => {
|
test('markdown indentation', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
|
|
||||||
const initText = `* first\n* second\n* third\n* last`;
|
const initText = `* first\n* second\n* third\n* last`;
|
||||||
|
|
||||||
const page = await context.newPage();
|
|
||||||
const response = await page.goto('/user2/repo1/issues/new');
|
const response = await page.goto('/user2/repo1/issues/new');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -116,12 +108,9 @@ test('markdown indentation', async ({browser}, workerInfo) => {
|
||||||
await expect(textarea).toHaveValue(initText);
|
await expect(textarea).toHaveValue(initText);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('markdown list continuation', async ({browser}, workerInfo) => {
|
test('markdown list continuation', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
|
|
||||||
const initText = `* first\n* second\n* third\n* last`;
|
const initText = `* first\n* second\n* third\n* last`;
|
||||||
|
|
||||||
const page = await context.newPage();
|
|
||||||
const response = await page.goto('/user2/repo1/issues/new');
|
const response = await page.goto('/user2/repo1/issues/new');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -213,10 +202,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('markdown insert table', async ({browser}, workerInfo) => {
|
test('markdown insert table', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
|
|
||||||
const page = await context.newPage();
|
|
||||||
const response = await page.goto('/user2/repo1/issues/new');
|
const response = await page.goto('/user2/repo1/issues/new');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
|
|
@ -5,16 +5,13 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, login} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
import {validate_form} from './shared/forms.ts';
|
import {validate_form} from './shared/forms.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('org team settings', async ({browser}, workerInfo) => {
|
test('org team settings', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/org/org3/teams/team1/edit');
|
const response = await page.goto('/org/org3/teams/team1/edit');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,11 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test('Follow actions', async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
|
test('Follow actions', async ({page}) => {
|
||||||
await page.goto('/user1');
|
await page.goto('/user1');
|
||||||
|
|
||||||
// Check if following and then unfollowing works.
|
// Check if following and then unfollowing works.
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect, type Locator} from '@playwright/test';
|
import {expect, type Locator} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
const assertReactionCounts = (comment: Locator, counts: unknown) =>
|
const assertReactionCounts = (comment: Locator, counts: unknown) =>
|
||||||
expect(async () => {
|
expect(async () => {
|
||||||
|
@ -26,6 +24,7 @@ const assertReactionCounts = (comment: Locator, counts: unknown) =>
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
// eslint-disable-next-line playwright/no-standalone-expect
|
||||||
return expect(reactions).toStrictEqual(counts);
|
return expect(reactions).toStrictEqual(counts);
|
||||||
}).toPass();
|
}).toPass();
|
||||||
|
|
||||||
|
@ -35,10 +34,7 @@ async function toggleReaction(menu: Locator, reaction: string) {
|
||||||
await menu.locator(`[role=menuitem][data-reaction-content="${reaction}"]`).click();
|
await menu.locator(`[role=menuitem][data-reaction-content="${reaction}"]`).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Reaction Selectors', async ({browser}, workerInfo) => {
|
test('Reaction Selectors', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
const response = await page.goto('/user2/repo1/issues/1');
|
const response = await page.goto('/user2/repo1/issues/1');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
|
|
@ -9,24 +9,18 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
import {validate_form} from './shared/forms.ts';
|
import {validate_form} from './shared/forms.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test.describe.configure({
|
test.describe.configure({
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
|
|
||||||
test('External Release Attachments', async ({browser, isMobile}, workerInfo) => {
|
test('External Release Attachments', async ({page, isMobile}) => {
|
||||||
test.skip(isMobile);
|
test.skip(isMobile);
|
||||||
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
/** @type {import('@playwright/test').Page} */
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
// Click "New Release"
|
// Click "New Release"
|
||||||
await page.goto('/user2/repo2/releases');
|
await page.goto('/user2/repo2/releases');
|
||||||
await page.click('.button.small.primary');
|
await page.click('.button.small.primary');
|
||||||
|
|
|
@ -5,13 +5,9 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect, type Page} from '@playwright/test';
|
import {expect, type Page} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, login} from './utils_e2e.ts';
|
import {save_visual, test} from './utils_e2e.ts';
|
||||||
import {accessibilityCheck} from './shared/accessibility.ts';
|
import {accessibilityCheck} from './shared/accessibility.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
async function assertSelectedLines(page: Page, nums: string[]) {
|
async function assertSelectedLines(page: Page, nums: string[]) {
|
||||||
const pageAssertions = async () => {
|
const pageAssertions = async () => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -81,8 +77,10 @@ test('Readable diff', async ({page}, workerInfo) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Username highlighted in commits', async ({browser}, workerInfo) => {
|
test.describe('As authenticated user', () => {
|
||||||
const page = await login({browser}, workerInfo);
|
test.use({user: 'user2'});
|
||||||
|
|
||||||
|
test('Username highlighted in commits', async ({page}) => {
|
||||||
await page.goto('/user2/mentions-highlighted/commits/branch/main');
|
await page.goto('/user2/mentions-highlighted/commits/branch/main');
|
||||||
// check first commit
|
// check first commit
|
||||||
await page.getByRole('link', {name: 'A commit message which'}).click();
|
await page.getByRole('link', {name: 'A commit message which'}).click();
|
||||||
|
@ -97,4 +95,5 @@ test('Username highlighted in commits', async ({browser}, workerInfo) => {
|
||||||
await expect(page.getByRole('link', {name: '@user1'})).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
await expect(page.getByRole('link', {name: '@user1'})).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||||
await accessibilityCheck({page}, ['.commit-header'], [], []);
|
await accessibilityCheck({page}, ['.commit-header'], [], []);
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,15 +3,13 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, load_logged_in_context} from './utils_e2e.ts';
|
import {test, save_visual, test_context} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(({browser}, workerInfo) => login_user(browser, workerInfo, 'user2'));
|
test.use({user: 'user2'});
|
||||||
|
|
||||||
test('Migration Progress Page', async ({page: unauthedPage, browser}, workerInfo) => {
|
test('Migration Progress Page', async ({page, browser}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Flaky actionability checks on Mobile Safari');
|
||||||
|
|
||||||
const page = await (await load_logged_in_context(browser, workerInfo, 'user2')).newPage();
|
|
||||||
|
|
||||||
expect((await page.goto('/user2/invalidrepo'))?.status(), 'repo should not exist yet').toBe(404);
|
expect((await page.goto('/user2/invalidrepo'))?.status(), 'repo should not exist yet').toBe(404);
|
||||||
|
|
||||||
await page.goto('/repo/migrate?service_type=1');
|
await page.goto('/repo/migrate?service_type=1');
|
||||||
|
@ -23,10 +21,12 @@ test('Migration Progress Page', async ({page: unauthedPage, browser}, workerInfo
|
||||||
await form.locator('button.primary').click({timeout: 5000});
|
await form.locator('button.primary').click({timeout: 5000});
|
||||||
await expect(page).toHaveURL('user2/invalidrepo');
|
await expect(page).toHaveURL('user2/invalidrepo');
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
// page screenshot of unauthedPage is checked automatically after the test
|
// page screenshot of unauthenticatedPage is checked automatically after the test
|
||||||
|
|
||||||
expect((await unauthedPage.goto('/user2/invalidrepo'))?.status(), 'public migration page should be accessible').toBe(200);
|
const ctx = await test_context(browser);
|
||||||
await expect(unauthedPage.locator('#repo_migrating_progress')).toBeVisible();
|
const unauthenticatedPage = await ctx.newPage();
|
||||||
|
expect((await unauthenticatedPage.goto('/user2/invalidrepo'))?.status(), 'public migration page should be accessible').toBe(200);
|
||||||
|
await expect(unauthenticatedPage.locator('#repo_migrating_progress')).toBeVisible();
|
||||||
|
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.locator('#repo_migrating_failed')).toBeVisible();
|
await expect(page.locator('#repo_migrating_failed')).toBeVisible();
|
||||||
|
|
|
@ -4,15 +4,12 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, dynamic_id, save_visual, login_user, login} from './utils_e2e.ts';
|
import {test, dynamic_id, save_visual} from './utils_e2e.ts';
|
||||||
import {validate_form} from './shared/forms.ts';
|
import {validate_form} from './shared/forms.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('New repo: invalid', async ({browser}, workerInfo) => {
|
test('New repo: invalid', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/repo/create');
|
const response = await page.goto('/repo/create');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
// check that relevant form content is hidden or available
|
// check that relevant form content is hidden or available
|
||||||
|
@ -28,8 +25,7 @@ test('New repo: invalid', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New repo: initialize', async ({browser}, workerInfo) => {
|
test('New repo: initialize', async ({page}, workerInfo) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/repo/create');
|
const response = await page.goto('/repo/create');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
// check that relevant form content is hidden or available
|
// check that relevant form content is hidden or available
|
||||||
|
@ -62,8 +58,7 @@ test('New repo: initialize', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New repo: initialize later', async ({browser}, workerInfo) => {
|
test('New repo: initialize later', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/repo/create');
|
const response = await page.goto('/repo/create');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -97,9 +92,8 @@ test('New repo: initialize later', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New repo: from template', async ({browser}, workerInfo) => {
|
test('New repo: from template', async ({page}, workerInfo) => {
|
||||||
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'WebKit browsers seem to have CORS issues with localhost here.');
|
test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'WebKit browsers seem to have CORS issues with localhost here.');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/repo/create');
|
const response = await page.goto('/repo/create');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -114,8 +108,7 @@ test('New repo: from template', async ({browser}, workerInfo) => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('New repo: label set', async ({browser}, workerInfo) => {
|
test('New repo: label set', async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
await page.goto('/repo/create');
|
await page.goto('/repo/create');
|
||||||
|
|
||||||
const reponame = dynamic_id();
|
const reponame = dynamic_id();
|
||||||
|
|
|
@ -7,16 +7,13 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, save_visual, login_user, login} from './utils_e2e.ts';
|
import {test, save_visual} from './utils_e2e.ts';
|
||||||
import {validate_form} from './shared/forms.ts';
|
import {validate_form} from './shared/forms.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.use({user: 'user2'});
|
||||||
await login_user(browser, workerInfo, 'user2');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('repo webhook settings', async ({browser}, workerInfo) => {
|
test('repo webhook settings', async ({page}, workerInfo) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
test.skip(workerInfo.project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/repo1/settings/hooks/forgejo/new');
|
const response = await page.goto('/user2/repo1/settings/hooks/forgejo/new');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -35,9 +32,8 @@ test('repo webhook settings', async ({browser}, workerInfo) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('repo branch protection settings', () => {
|
test.describe('repo branch protection settings', () => {
|
||||||
test('form', async ({browser}, workerInfo) => {
|
test('form', async ({page}, {project}) => {
|
||||||
test.skip(workerInfo.project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
test.skip(project.name === 'Mobile Safari', 'Cannot get it to work - as usual');
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
const response = await page.goto('/user2/repo1/settings/branches/edit');
|
const response = await page.goto('/user2/repo1/settings/branches/edit');
|
||||||
expect(response?.status()).toBe(200);
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
@ -56,8 +52,7 @@ test.describe('repo branch protection settings', () => {
|
||||||
await save_visual(page);
|
await save_visual(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterEach(async ({browser}, workerInfo) => {
|
test.afterEach(async ({page}) => {
|
||||||
const page = await login({browser}, workerInfo);
|
|
||||||
// delete the rule for the next test
|
// delete the rule for the next test
|
||||||
await page.goto('/user2/repo1/settings/branches/');
|
await page.goto('/user2/repo1/settings/branches/');
|
||||||
await page.waitForLoadState('domcontentloaded');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
|
|
@ -5,19 +5,12 @@
|
||||||
// @watch end
|
// @watch end
|
||||||
|
|
||||||
import {expect} from '@playwright/test';
|
import {expect} from '@playwright/test';
|
||||||
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
|
import {test} from './utils_e2e.ts';
|
||||||
|
|
||||||
test.beforeAll(async ({browser}, workerInfo) => {
|
test.describe('desktop viewport as user 2', () => {
|
||||||
await login_user(browser, workerInfo, 'user2');
|
test.use({user: 'user2', viewport: {width: 1920, height: 300}});
|
||||||
});
|
|
||||||
|
|
||||||
test.describe('desktop viewport', () => {
|
|
||||||
test.use({viewport: {width: 1920, height: 300}});
|
|
||||||
|
|
||||||
test('Settings button on right of repo header', async ({browser}, workerInfo) => {
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
|
test('Settings button on right of repo header', async ({page}) => {
|
||||||
await page.goto('/user2/repo1');
|
await page.goto('/user2/repo1');
|
||||||
|
|
||||||
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
||||||
|
@ -27,24 +20,7 @@ test.describe('desktop viewport', () => {
|
||||||
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings button on right of repo header also when add more button is shown', async ({browser}, workerInfo) => {
|
test('Settings button on right of org header', async ({page}) => {
|
||||||
await login_user(browser, workerInfo, 'user12');
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user12');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('/user12/repo10');
|
|
||||||
|
|
||||||
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
|
||||||
await expect(settingsBtn).toBeVisible();
|
|
||||||
await expect(settingsBtn).toHaveClass(/right/);
|
|
||||||
|
|
||||||
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Settings button on right of org header', async ({browser}, workerInfo) => {
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('/org3');
|
await page.goto('/org3');
|
||||||
|
|
||||||
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
||||||
|
@ -53,6 +29,24 @@ test.describe('desktop viewport', () => {
|
||||||
|
|
||||||
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('desktop viewport as user12', () => {
|
||||||
|
test.use({user: 'user12', viewport: {width: 1920, height: 300}});
|
||||||
|
|
||||||
|
test('Settings button on right of repo header also when add more button is shown', async ({page}) => {
|
||||||
|
await page.goto('/user12/repo10');
|
||||||
|
|
||||||
|
const settingsBtn = page.locator('.overflow-menu-items>#settings-btn');
|
||||||
|
await expect(settingsBtn).toBeVisible();
|
||||||
|
await expect(settingsBtn).toHaveClass(/right/);
|
||||||
|
|
||||||
|
await expect(page.locator('.overflow-menu-button')).toHaveCount(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('desktop viewport, unauthenticated', () => {
|
||||||
|
test.use({viewport: {width: 1920, height: 300}});
|
||||||
|
|
||||||
test('User overview overflow menu should not be influenced', async ({page}) => {
|
test('User overview overflow menu should not be influenced', async ({page}) => {
|
||||||
await page.goto('/user2');
|
await page.goto('/user2');
|
||||||
|
@ -64,12 +58,9 @@ test.describe('desktop viewport', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('small viewport', () => {
|
test.describe('small viewport', () => {
|
||||||
test.use({viewport: {width: 800, height: 300}});
|
test.use({user: 'user2', viewport: {width: 800, height: 300}});
|
||||||
|
|
||||||
test('Settings button in overflow menu of repo header', async ({browser}, workerInfo) => {
|
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
|
test('Settings button in overflow menu of repo header', async ({page}) => {
|
||||||
await page.goto('/user2/repo1');
|
await page.goto('/user2/repo1');
|
||||||
|
|
||||||
await expect(page.locator('.overflow-menu-items>#settings-btn')).toHaveCount(0);
|
await expect(page.locator('.overflow-menu-items>#settings-btn')).toHaveCount(0);
|
||||||
|
@ -89,10 +80,7 @@ test.describe('small viewport', () => {
|
||||||
expect(Array.from(new Set(items))).toHaveLength(items.length);
|
expect(Array.from(new Set(items))).toHaveLength(items.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Settings button in overflow menu of org header', async ({browser}, workerInfo) => {
|
test('Settings button in overflow menu of org header', async ({page}) => {
|
||||||
const context = await load_logged_in_context(browser, workerInfo, 'user2');
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('/org3');
|
await page.goto('/org3');
|
||||||
|
|
||||||
await expect(page.locator('.overflow-menu-items>#settings-btn')).toHaveCount(0);
|
await expect(page.locator('.overflow-menu-items>#settings-btn')).toHaveCount(0);
|
||||||
|
@ -111,6 +99,10 @@ test.describe('small viewport', () => {
|
||||||
const items = shownItems.concat(overflowItems);
|
const items = shownItems.concat(overflowItems);
|
||||||
expect(Array.from(new Set(items))).toHaveLength(items.length);
|
expect(Array.from(new Set(items))).toHaveLength(items.length);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('small viewport, unauthenticated', () => {
|
||||||
|
test.use({viewport: {width: 800, height: 300}});
|
||||||
|
|
||||||
test('User overview overflow menu should not be influenced', async ({page}) => {
|
test('User overview overflow menu should not be influenced', async ({page}) => {
|
||||||
await page.goto('/user2');
|
await page.goto('/user2');
|
||||||
|
|
|
@ -1,9 +1,31 @@
|
||||||
import {expect, test as baseTest, type Browser, type BrowserContextOptions, type APIRequestContext, type TestInfo, type Page} from '@playwright/test';
|
import {expect, test as baseTest, type Browser, type BrowserContextOptions, type APIRequestContext, type TestInfo, type Page} from '@playwright/test';
|
||||||
|
|
||||||
export const test = baseTest.extend({
|
import * as path from 'node:path';
|
||||||
context: async ({browser}, use) => {
|
|
||||||
return use(await test_context(browser));
|
const AUTH_PATH = 'tests/e2e/.auth';
|
||||||
|
|
||||||
|
type AuthScope = 'logout' | 'shared' | 'webauthn';
|
||||||
|
|
||||||
|
export type TestOptions = {
|
||||||
|
forEachTest: void
|
||||||
|
user: string | null;
|
||||||
|
authScope: AuthScope;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const test = baseTest.extend<TestOptions>({
|
||||||
|
context: async ({browser, user, authScope, contextOptions}, use, {project}) => {
|
||||||
|
if (user && authScope) {
|
||||||
|
const browserName = project.name.toLowerCase().replace(' ', '-');
|
||||||
|
contextOptions.storageState = path.join(AUTH_PATH, `state-${browserName}-${user}-${authScope}.json`);
|
||||||
|
} else {
|
||||||
|
// if no user is given, ensure to have clean state
|
||||||
|
contextOptions.storageState = {cookies: [], origins: []};
|
||||||
|
}
|
||||||
|
|
||||||
|
return use(await test_context(browser, contextOptions));
|
||||||
},
|
},
|
||||||
|
user: null,
|
||||||
|
authScope: 'shared',
|
||||||
// see https://playwright.dev/docs/test-fixtures#adding-global-beforeeachaftereach-hooks
|
// see https://playwright.dev/docs/test-fixtures#adding-global-beforeeachaftereach-hooks
|
||||||
forEachTest: [async ({page}, use) => {
|
forEachTest: [async ({page}, use) => {
|
||||||
await use();
|
await use();
|
||||||
|
@ -15,7 +37,7 @@ export const test = baseTest.extend({
|
||||||
}, {auto: true}],
|
}, {auto: true}],
|
||||||
});
|
});
|
||||||
|
|
||||||
async function test_context(browser: Browser, options?: BrowserContextOptions) {
|
export async function test_context(browser: Browser, options?: BrowserContextOptions) {
|
||||||
const context = await browser.newContext(options);
|
const context = await browser.newContext(options);
|
||||||
|
|
||||||
context.on('page', (page) => {
|
context.on('page', (page) => {
|
||||||
|
|
|
@ -5,17 +5,27 @@ package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
modules_session "code.gitea.io/gitea/modules/session"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"code.forgejo.org/go-chi/session"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +35,8 @@ func onForgejoRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare .
|
||||||
if len(prepare) == 0 || prepare[0] {
|
if len(prepare) == 0 || prepare[0] {
|
||||||
defer tests.PrepareTestEnv(t, 1)()
|
defer tests.PrepareTestEnv(t, 1)()
|
||||||
}
|
}
|
||||||
|
createSessions(t)
|
||||||
|
|
||||||
s := http.Server{
|
s := http.Server{
|
||||||
Handler: testE2eWebRoutes,
|
Handler: testE2eWebRoutes,
|
||||||
}
|
}
|
||||||
|
@ -64,3 +76,118 @@ func onForgejoRun(t *testing.T, callback func(*testing.T, *url.URL), prepare ...
|
||||||
callback(t.(*testing.T), u)
|
callback(t.(*testing.T), u)
|
||||||
}, prepare...)
|
}, prepare...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createSessions(t testing.TB) {
|
||||||
|
t.Helper()
|
||||||
|
// copied from playwright.config.ts
|
||||||
|
browsers := []string{
|
||||||
|
"chromium",
|
||||||
|
"firefox",
|
||||||
|
"webkit",
|
||||||
|
"Mobile Chrome",
|
||||||
|
"Mobile Safari",
|
||||||
|
}
|
||||||
|
scopes := []string{
|
||||||
|
"shared",
|
||||||
|
}
|
||||||
|
users := []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user12",
|
||||||
|
"user40",
|
||||||
|
}
|
||||||
|
|
||||||
|
authState := filepath.Join(filepath.Dir(setting.AppPath), "tests", "e2e", ".auth")
|
||||||
|
err := os.RemoveAll(authState)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = os.MkdirAll(authState, os.ModePerm)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
createSessionCookie := stateHelper(t)
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: strings.ToLower(user)})
|
||||||
|
for _, browser := range browsers {
|
||||||
|
for _, scope := range scopes {
|
||||||
|
stateFile := strings.ReplaceAll(strings.ToLower(fmt.Sprintf("state-%s-%s-%s.json", browser, user, scope)), " ", "-")
|
||||||
|
createSessionCookie(filepath.Join(authState, stateFile), u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateHelper(t testing.TB) func(stateFile string, user *user_model.User) {
|
||||||
|
type Cookie struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Expires int `json:"expires"`
|
||||||
|
HTTPOnly bool `json:"httpOnly"`
|
||||||
|
Secure bool `json:"secure"`
|
||||||
|
SameSite string `json:"sameSite"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BrowserState struct {
|
||||||
|
Cookies []Cookie `json:"cookies"`
|
||||||
|
Origins []string `json:"origins"`
|
||||||
|
}
|
||||||
|
|
||||||
|
options := session.Options{
|
||||||
|
Provider: setting.SessionConfig.Provider,
|
||||||
|
ProviderConfig: setting.SessionConfig.ProviderConfig,
|
||||||
|
CookieName: setting.SessionConfig.CookieName,
|
||||||
|
CookiePath: setting.SessionConfig.CookiePath,
|
||||||
|
Gclifetime: setting.SessionConfig.Gclifetime,
|
||||||
|
Maxlifetime: setting.SessionConfig.Maxlifetime,
|
||||||
|
Secure: setting.SessionConfig.Secure,
|
||||||
|
SameSite: setting.SessionConfig.SameSite,
|
||||||
|
Domain: setting.SessionConfig.Domain,
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := session.PrepareOptions([]session.Options{options})
|
||||||
|
|
||||||
|
vsp := modules_session.VirtualSessionProvider{}
|
||||||
|
err := vsp.Init(opt.Maxlifetime, opt.ProviderConfig)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return func(stateFile string, user *user_model.User) {
|
||||||
|
buf := make([]byte, opt.IDLength/2)
|
||||||
|
_, err = rand.Read(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sessionID := hex.EncodeToString(buf)
|
||||||
|
|
||||||
|
s, err := vsp.Read(sessionID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.Set("uid", user.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = s.Release()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
state := BrowserState{
|
||||||
|
Cookies: []Cookie{
|
||||||
|
{
|
||||||
|
Name: opt.CookieName,
|
||||||
|
Value: sessionID,
|
||||||
|
Domain: setting.Domain,
|
||||||
|
Path: "/",
|
||||||
|
Expires: -1,
|
||||||
|
HTTPOnly: true,
|
||||||
|
Secure: false,
|
||||||
|
SameSite: "Lax",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Origins: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(state)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = os.WriteFile(stateFile, jsonData, 0o644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue