chore: add extra playwright rules

This commit is contained in:
Gusted 2024-10-23 16:22:25 +02:00
parent cdbbdacb7d
commit 94e4f8648b
17 changed files with 34 additions and 32 deletions

View file

@ -1125,9 +1125,11 @@ export default [{
allowConditional: true, allowConditional: true,
}, },
], ],
'playwright/no-useless-await': [2],
'playwright/prefer-comparison-matcher': [2], 'playwright/prefer-comparison-matcher': [2],
'playwright/prefer-equality-matcher': [2], 'playwright/prefer-equality-matcher': [2],
'playwright/prefer-native-locators': [2],
'playwright/prefer-to-contain': [2], 'playwright/prefer-to-contain': [2],
'playwright/prefer-to-have-length': [2], 'playwright/prefer-to-have-length': [2],
'playwright/require-to-throw-message': [2], 'playwright/require-to-throw-message': [2],

View file

@ -15,7 +15,7 @@ test('Correct link and tooltip', async ({browser}, workerInfo) => {
const context = await load_logged_in_context(browser, workerInfo, 'user2'); const context = await load_logged_in_context(browser, workerInfo, 'user2');
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/?repo-search-query=test_workflows'); const response = await page.goto('/?repo-search-query=test_workflows');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');

View file

@ -15,21 +15,21 @@ test.beforeAll(async ({browser}, workerInfo) => {
test('Load Homepage', async ({page}) => { test('Load Homepage', async ({page}) => {
const response = await page.goto('/'); const response = await page.goto('/');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await expect(page).toHaveTitle(/^Forgejo: Beyond coding. We Forge.\s*$/); await expect(page).toHaveTitle(/^Forgejo: Beyond coding. We Forge.\s*$/);
await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg'); await expect(page.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg');
}); });
test('Register Form', async ({page}, workerInfo) => { test('Register Form', async ({page}, workerInfo) => {
const response = await page.goto('/user/sign_up'); const response = await page.goto('/user/sign_up');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`); await page.type('input[name=user_name]', `e2e-test-${workerInfo.workerIndex}`);
await page.type('input[name=email]', `e2e-test-${workerInfo.workerIndex}@test.com`); await page.type('input[name=email]', `e2e-test-${workerInfo.workerIndex}@test.com`);
await page.type('input[name=password]', 'test123test123'); await page.type('input[name=password]', 'test123test123');
await page.type('input[name=retype]', 'test123test123'); await page.type('input[name=retype]', 'test123test123');
await page.click('form button.ui.primary.button:visible'); await page.click('form button.ui.primary.button:visible');
// Make sure we routed to the home page. Else login failed. // Make sure we routed to the home page. Else login failed.
await expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url()).toBe(`${workerInfo.project.use.baseURL}/`);
await expect(page.locator('.secondary-nav span>img.ui.avatar')).toBeVisible(); await expect(page.locator('.secondary-nav span>img.ui.avatar')).toBeVisible();
await expect(page.locator('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!'); await expect(page.locator('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!');

View file

@ -42,5 +42,5 @@ test('Explore view taborder', async ({page}) => {
break; break;
} }
} }
await expect(res).toBe(exp); expect(res).toBe(exp);
}); });

View file

@ -49,7 +49,7 @@ test('Hyperlink paste behaviour', async ({browser}, workerInfo) => {
test('Always focus edit tab first on edit', async ({browser}, workerInfo) => { test('Always focus edit tab first on edit', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo); const page = await login({browser}, workerInfo);
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
// Switch to preview tab and save // Switch to preview tab and save
await page.click('#issue-1 .comment-container .context-menu'); await page.click('#issue-1 .comment-container .context-menu');

View file

@ -39,7 +39,7 @@ test('Pull: Toggle WIP', async ({browser}, 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 page = await login({browser}, workerInfo);
const response = await page.goto('/user2/repo1/pulls/5'); const response = await page.goto('/user2/repo1/pulls/5');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
// initial state // initial state
await check_wip({page}, false); await check_wip({page}, false);
// toggle to WIP // toggle to WIP
@ -82,7 +82,7 @@ test('Issue: Labels', async ({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');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
// preconditions // preconditions
await expect(labelList.filter({hasText: 'label1'})).toBeVisible(); await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
await expect(labelList.filter({hasText: 'label2'})).toBeHidden(); await expect(labelList.filter({hasText: 'label2'})).toBeHidden();
@ -110,7 +110,7 @@ test('Issue: Assignees', async ({browser}, workerInfo) => {
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a'); const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item a');
const response = await page.goto('/org3/repo3/issues/1'); const response = await page.goto('/org3/repo3/issues/1');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
// preconditions // preconditions
await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible(); await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible();
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden(); await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
@ -153,7 +153,7 @@ test('New Issue: Assignees', async ({browser}, workerInfo) => {
const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item'); const assigneesList = page.locator('.issue-content-right .assignees.list .selected .item');
const response = await page.goto('/org3/repo3/issues/new'); const response = await page.goto('/org3/repo3/issues/new');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
// preconditions // preconditions
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible(); await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible();
await expect(assigneesList.filter({hasText: 'user2'})).toBeHidden(); await expect(assigneesList.filter({hasText: 'user2'})).toBeHidden();
@ -191,7 +191,7 @@ test('Issue: Milestone', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo); const page = await login({browser}, workerInfo);
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const selectedMilestone = page.locator('.issue-content-right .select-milestone.list'); const selectedMilestone = page.locator('.issue-content-right .select-milestone.list');
const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown'); const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown');
@ -215,7 +215,7 @@ test('New Issue: Milestone', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo); const page = await login({browser}, workerInfo);
const response = await page.goto('/user2/repo1/issues/new'); const response = await page.goto('/user2/repo1/issues/new');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const selectedMilestone = page.locator('.issue-content-right .select-milestone.list'); const selectedMilestone = page.locator('.issue-content-right .select-milestone.list');
const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown'); const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown');

View file

@ -19,7 +19,7 @@ test('markdown indentation', async ({browser}, workerInfo) => {
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/user2/repo1/issues/new'); const response = await page.goto('/user2/repo1/issues/new');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const textarea = page.locator('textarea[name=content]'); const textarea = page.locator('textarea[name=content]');
const tab = ' '; const tab = ' ';
@ -92,7 +92,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => {
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/user2/repo1/issues/new'); const response = await page.goto('/user2/repo1/issues/new');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const textarea = page.locator('textarea[name=content]'); const textarea = page.locator('textarea[name=content]');
const tab = ' '; const tab = ' ';

View file

@ -9,7 +9,7 @@ import {test} from './utils_e2e.js';
test('markup with #xyz-mode-only', async ({page}) => { test('markup with #xyz-mode-only', async ({page}) => {
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');
const comment = page.locator('.comment-body>.markup', {hasText: 'test markup light/dark-mode-only'}); const comment = page.locator('.comment-body>.markup', {hasText: 'test markup light/dark-mode-only'});

View file

@ -18,7 +18,7 @@ test('org team settings', async ({browser}, 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 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');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.locator('input[name="permission"][value="admin"]').click(); await page.locator('input[name="permission"][value="admin"]').click();
await expect(page.locator('.hide-unless-checked')).toBeHidden(); await expect(page.locator('.hide-unless-checked')).toBeHidden();

View file

@ -42,7 +42,7 @@ test('Reaction Selectors', async ({browser}, workerInfo) => {
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/user2/repo1/issues/1'); const response = await page.goto('/user2/repo1/issues/1');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const comment = page.locator('.comment#issuecomment-2').first(); const comment = page.locator('.comment#issuecomment-2').first();

View file

@ -42,7 +42,7 @@ test('Line Range Selection', async ({browser}, workerInfo) => {
const filePath = '/user2/repo1/src/branch/master/README.md?display=source'; const filePath = '/user2/repo1/src/branch/master/README.md?display=source';
const response = await page.goto(filePath); const response = await page.goto(filePath);
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await assertSelectedLines(page, []); await assertSelectedLines(page, []);
await page.locator('span#L1').click(); await page.locator('span#L1').click();
@ -72,7 +72,7 @@ test('Readable diff', async ({page}, workerInfo) => {
]; ];
for (const thisDiff of expectedDiffs) { for (const thisDiff of expectedDiffs) {
const response = await page.goto('/user2/diff-test/commits/branch/main'); const response = await page.goto('/user2/diff-test/commits/branch/main');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
await page.getByText(`Patch: ${thisDiff.id}`).click(); await page.getByText(`Patch: ${thisDiff.id}`).click();
if (thisDiff.removed) { if (thisDiff.removed) {
await expect(page.getByText(thisDiff.removed, {exact: true})).toHaveClass(/removed-code/); await expect(page.getByText(thisDiff.removed, {exact: true})).toHaveClass(/removed-code/);

View file

@ -24,7 +24,7 @@ test('Switch branch', async ({browser}, workerInfo) => {
const context = await load_logged_in_context(browser, workerInfo, 'user2'); const context = await load_logged_in_context(browser, workerInfo, 'user2');
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/user2/repo1/graph'); const response = await page.goto('/user2/repo1/graph');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.click('#flow-select-refs-dropdown'); await page.click('#flow-select-refs-dropdown');
const input = page.locator('#flow-select-refs-dropdown'); const input = page.locator('#flow-select-refs-dropdown');

View file

@ -14,7 +14,7 @@ test('Migration Progress Page', async ({page: unauthedPage, browser}, workerInfo
const page = await (await load_logged_in_context(browser, workerInfo, 'user2')).newPage(); const page = await (await load_logged_in_context(browser, workerInfo, 'user2')).newPage();
await 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');
@ -24,7 +24,7 @@ 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 expect((await unauthedPage.goto('/user2/invalidrepo'))?.status(), 'public migration page should be accessible').toBe(200); expect((await unauthedPage.goto('/user2/invalidrepo'))?.status(), 'public migration page should be accessible').toBe(200);
await expect(unauthedPage.locator('#repo_migrating_progress')).toBeVisible(); await expect(unauthedPage.locator('#repo_migrating_progress')).toBeVisible();
await page.reload(); await page.reload();

View file

@ -20,7 +20,7 @@ test('repo webhook settings', async ({browser}, 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 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');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.locator('input[name="events"][value="choose_events"]').click(); await page.locator('input[name="events"][value="choose_events"]').click();
await expect(page.locator('.hide-unless-checked')).toBeVisible(); await expect(page.locator('.hide-unless-checked')).toBeVisible();
@ -39,7 +39,7 @@ test.describe('repo branch protection settings', () => {
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 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');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await validate_form({page}, 'fieldset'); await validate_form({page}, 'fieldset');

View file

@ -26,7 +26,7 @@ export async function validate_form({page}, scope) {
// might be necessary to adjust in case colons are strictly necessary in help text // might be necessary to adjust in case colons are strictly necessary in help text
for (const l of await page.locator('label').all()) { for (const l of await page.locator('label').all()) {
const str = await l.textContent(); const str = await l.textContent();
await expect(str.split('\n')[0]).not.toContain(':'); expect(str.split('\n')[0]).not.toContain(':');
} }
// check that multiple help text are correctly aligned to each other // check that multiple help text are correctly aligned to each other
@ -36,9 +36,9 @@ export async function validate_form({page}, scope) {
const boxes = await Promise.all(helpLabels.map((help) => help.boundingBox())); const boxes = await Promise.all(helpLabels.map((help) => help.boundingBox()));
for (let i = 1; i < boxes.length; i++) { for (let i = 1; i < boxes.length; i++) {
// help texts vertically aligned on top of each other // help texts vertically aligned on top of each other
await expect(boxes[i].x).toBe(boxes[0].x); expect(boxes[i].x).toBe(boxes[0].x);
// help texts don't horizontally intersect each other // help texts don't horizontally intersect each other
await expect(boxes[i].y + boxes[i].height).toBeGreaterThanOrEqual(boxes[i - 1].y + boxes[i - 1].height); expect(boxes[i].y + boxes[i].height).toBeGreaterThanOrEqual(boxes[i - 1].y + boxes[i - 1].height);
} }
} }
} }

View file

@ -30,7 +30,7 @@ export async function login_user(browser, workerInfo, user) {
// Route to login page // Route to login page
// Note: this could probably be done more quickly with a POST // Note: this could probably be done more quickly with a POST
const response = await page.goto('/user/login'); const response = await page.goto('/user/login');
await expect(response?.status()).toBe(200); // Status OK expect(response?.status()).toBe(200); // Status OK
// Fill out form // Fill out form
await page.type('input[name=user_name]', user); await page.type('input[name=user_name]', user);
@ -39,7 +39,7 @@ export async function login_user(browser, workerInfo, user) {
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');
await expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`); expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`);
// Save state // Save state
await context.storageState({path: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); await context.storageState({path: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`});

View file

@ -18,7 +18,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
// Register a security key. // Register a security key.
let response = await page.goto('/user/settings/security'); let response = await page.goto('/user/settings/security');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
// https://github.com/microsoft/playwright/issues/7276#issuecomment-1516768428 // https://github.com/microsoft/playwright/issues/7276#issuecomment-1516768428
const cdpSession = await page.context().newCDPSession(page); const cdpSession = await page.context().newCDPSession(page);
@ -45,7 +45,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
// Login. // Login.
response = await page.goto('/user/login'); response = await page.goto('/user/login');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.getByLabel('Username or email address').fill(username); await page.getByLabel('Username or email address').fill(username);
await page.getByLabel('Password').fill('password'); await page.getByLabel('Password').fill('password');
@ -55,7 +55,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
// Cleanup. // Cleanup.
response = await page.goto('/user/settings/security'); response = await page.goto('/user/settings/security');
await expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
await page.getByRole('button', {name: 'Remove'}).click(); await page.getByRole('button', {name: 'Remove'}).click();
await page.getByRole('button', {name: 'Yes'}).click(); await page.getByRole('button', {name: 'Yes'}).click();
await page.waitForURL(`${workerInfo.project.use.baseURL}/user/settings/security`); await page.waitForURL(`${workerInfo.project.use.baseURL}/user/settings/security`);