diff --git a/eslint.config.mjs b/eslint.config.mjs index 389f7c3cd1..ee2aa5da27 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1125,9 +1125,11 @@ export default [{ allowConditional: true, }, ], + 'playwright/no-useless-await': [2], 'playwright/prefer-comparison-matcher': [2], 'playwright/prefer-equality-matcher': [2], + 'playwright/prefer-native-locators': [2], 'playwright/prefer-to-contain': [2], 'playwright/prefer-to-have-length': [2], 'playwright/require-to-throw-message': [2], diff --git a/package-lock.json b/package-lock.json index ed6ca6f647..c974973366 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "eslint-plugin-import-x": "4.3.1", "eslint-plugin-no-jquery": "3.0.2", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-playwright": "1.6.2", + "eslint-plugin-playwright": "2.0.0", "eslint-plugin-regexp": "2.6.0", "eslint-plugin-sonarjs": "2.0.3", "eslint-plugin-unicorn": "56.0.0", @@ -8294,9 +8294,9 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-1.6.2.tgz", - "integrity": "sha512-mraN4Em3b5jLt01q7qWPyLg0Q5v3KAWfJSlEWwldyUXoa7DSPrBR4k6B6LROLqipsG8ndkwWMdjl1Ffdh15tag==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.0.0.tgz", + "integrity": "sha512-nPa44nSp48mp/U+GSneabrhlyIyGvrcv+Z14u6sgno+jX8N0bH+ooSLEC1L6dvMDSHs7tj+kMIbls3l8gCJJSg==", "dev": true, "license": "MIT", "workspaces": [ @@ -8309,13 +8309,7 @@ "node": ">=16.6.0" }, "peerDependencies": { - "eslint": ">=8.40.0", - "eslint-plugin-jest": ">=25" - }, - "peerDependenciesMeta": { - "eslint-plugin-jest": { - "optional": true - } + "eslint": ">=8.40.0" } }, "node_modules/eslint-plugin-playwright/node_modules/globals": { diff --git a/package.json b/package.json index eecff8e617..4a0dfafc3e 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "eslint-plugin-import-x": "4.3.1", "eslint-plugin-no-jquery": "3.0.2", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-playwright": "1.6.2", + "eslint-plugin-playwright": "2.0.0", "eslint-plugin-regexp": "2.6.0", "eslint-plugin-sonarjs": "2.0.3", "eslint-plugin-unicorn": "56.0.0", diff --git a/tests/e2e/dashboard-ci-status.test.e2e.js b/tests/e2e/dashboard-ci-status.test.e2e.js index ec61bfac76..289430055c 100644 --- a/tests/e2e/dashboard-ci-status.test.e2e.js +++ b/tests/e2e/dashboard-ci-status.test.e2e.js @@ -15,7 +15,7 @@ test('Correct link and tooltip', async ({browser}, workerInfo) => { const context = await load_logged_in_context(browser, workerInfo, 'user2'); const page = await context.newPage(); 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'); diff --git a/tests/e2e/example.test.e2e.js b/tests/e2e/example.test.e2e.js index c163c8bb42..a413a218c6 100644 --- a/tests/e2e/example.test.e2e.js +++ b/tests/e2e/example.test.e2e.js @@ -15,21 +15,21 @@ test.beforeAll(async ({browser}, workerInfo) => { test('Load Homepage', async ({page}) => { 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.locator('.logo')).toHaveAttribute('src', '/assets/img/logo.svg'); }); test('Register Form', async ({page}, workerInfo) => { 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=email]', `e2e-test-${workerInfo.workerIndex}@test.com`); await page.type('input[name=password]', 'test123test123'); await page.type('input[name=retype]', 'test123test123'); await page.click('form button.ui.primary.button:visible'); // 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('.ui.positive.message.flash-success')).toHaveText('Account was successfully created. Welcome!'); diff --git a/tests/e2e/explore.test.e2e.js b/tests/e2e/explore.test.e2e.js index b64eca78e3..eb0c723f36 100644 --- a/tests/e2e/explore.test.e2e.js +++ b/tests/e2e/explore.test.e2e.js @@ -42,5 +42,5 @@ test('Explore view taborder', async ({page}) => { break; } } - await expect(res).toBe(exp); + expect(res).toBe(exp); }); diff --git a/tests/e2e/issue-comment.test.e2e.js b/tests/e2e/issue-comment.test.e2e.js index 55dccf1ebd..a21f00c511 100644 --- a/tests/e2e/issue-comment.test.e2e.js +++ b/tests/e2e/issue-comment.test.e2e.js @@ -49,7 +49,7 @@ test('Hyperlink paste behaviour', async ({browser}, workerInfo) => { test('Always focus edit tab first on edit', async ({browser}, workerInfo) => { const page = await login({browser}, workerInfo); 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 await page.click('#issue-1 .comment-container .context-menu'); diff --git a/tests/e2e/issue-sidebar.test.e2e.js b/tests/e2e/issue-sidebar.test.e2e.js index 7fd60555be..8b5fa59331 100644 --- a/tests/e2e/issue-sidebar.test.e2e.js +++ b/tests/e2e/issue-sidebar.test.e2e.js @@ -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'); const page = await login({browser}, workerInfo); 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 await check_wip({page}, false); // toggle to WIP @@ -82,7 +82,7 @@ test('Issue: Labels', async ({browser}, workerInfo) => { // select label list in sidebar only const labelList = page.locator('.issue-content-right .labels-list a'); const response = await page.goto('/user2/repo1/issues/1'); - await expect(response?.status()).toBe(200); + expect(response?.status()).toBe(200); // preconditions await expect(labelList.filter({hasText: 'label1'})).toBeVisible(); 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 response = await page.goto('/org3/repo3/issues/1'); - await expect(response?.status()).toBe(200); + expect(response?.status()).toBe(200); // preconditions await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible(); 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 response = await page.goto('/org3/repo3/issues/new'); - await expect(response?.status()).toBe(200); + expect(response?.status()).toBe(200); // preconditions await expect(page.locator('.ui.assignees.list .item.no-select')).toBeVisible(); await expect(assigneesList.filter({hasText: 'user2'})).toBeHidden(); @@ -191,7 +191,7 @@ test('Issue: Milestone', async ({browser}, workerInfo) => { const page = await login({browser}, workerInfo); 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 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 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 milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown'); diff --git a/tests/e2e/markdown-editor.test.e2e.js b/tests/e2e/markdown-editor.test.e2e.js index a1f2a2d96c..37b8fecf36 100644 --- a/tests/e2e/markdown-editor.test.e2e.js +++ b/tests/e2e/markdown-editor.test.e2e.js @@ -19,7 +19,7 @@ test('markdown indentation', async ({browser}, workerInfo) => { const page = await context.newPage(); 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 tab = ' '; @@ -92,7 +92,7 @@ test('markdown list continuation', async ({browser}, workerInfo) => { const page = await context.newPage(); 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 tab = ' '; diff --git a/tests/e2e/markup.test.e2e.js b/tests/e2e/markup.test.e2e.js index a2b795e852..f7ec31709d 100644 --- a/tests/e2e/markup.test.e2e.js +++ b/tests/e2e/markup.test.e2e.js @@ -9,7 +9,7 @@ import {test} from './utils_e2e.js'; test('markup with #xyz-mode-only', async ({page}) => { const response = await page.goto('/user2/repo1/issues/1'); - await expect(response?.status()).toBe(200); + expect(response?.status()).toBe(200); await page.waitForLoadState('networkidle'); const comment = page.locator('.comment-body>.markup', {hasText: 'test markup light/dark-mode-only'}); diff --git a/tests/e2e/org-settings.test.e2e.js b/tests/e2e/org-settings.test.e2e.js index 2a0fe69608..21f34c123d 100644 --- a/tests/e2e/org-settings.test.e2e.js +++ b/tests/e2e/org-settings.test.e2e.js @@ -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'); const page = await login({browser}, workerInfo); 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 expect(page.locator('.hide-unless-checked')).toBeHidden(); diff --git a/tests/e2e/reaction-selectors.test.e2e.js b/tests/e2e/reaction-selectors.test.e2e.js index 5e0ea5b519..184f25fe18 100644 --- a/tests/e2e/reaction-selectors.test.e2e.js +++ b/tests/e2e/reaction-selectors.test.e2e.js @@ -42,7 +42,7 @@ test('Reaction Selectors', async ({browser}, workerInfo) => { const page = await context.newPage(); 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(); diff --git a/tests/e2e/repo-code.test.e2e.js b/tests/e2e/repo-code.test.e2e.js index 9d9653d2fe..fdb92762ff 100644 --- a/tests/e2e/repo-code.test.e2e.js +++ b/tests/e2e/repo-code.test.e2e.js @@ -42,7 +42,7 @@ test('Line Range Selection', async ({browser}, workerInfo) => { const filePath = '/user2/repo1/src/branch/master/README.md?display=source'; const response = await page.goto(filePath); - await expect(response?.status()).toBe(200); + expect(response?.status()).toBe(200); await assertSelectedLines(page, []); await page.locator('span#L1').click(); @@ -72,7 +72,7 @@ test('Readable diff', async ({page}, workerInfo) => { ]; for (const thisDiff of expectedDiffs) { 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(); if (thisDiff.removed) { await expect(page.getByText(thisDiff.removed, {exact: true})).toHaveClass(/removed-code/); diff --git a/tests/e2e/repo-commitgraph.test.e2e.js b/tests/e2e/repo-commitgraph.test.e2e.js index 7bb3e1f23f..f06c68a55d 100644 --- a/tests/e2e/repo-commitgraph.test.e2e.js +++ b/tests/e2e/repo-commitgraph.test.e2e.js @@ -24,7 +24,7 @@ test('Switch branch', async ({browser}, workerInfo) => { const context = await load_logged_in_context(browser, workerInfo, 'user2'); const page = await context.newPage(); 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'); const input = page.locator('#flow-select-refs-dropdown'); diff --git a/tests/e2e/repo-migrate.test.e2e.js b/tests/e2e/repo-migrate.test.e2e.js index 7a9fc08fb2..2ad4400340 100644 --- a/tests/e2e/repo-migrate.test.e2e.js +++ b/tests/e2e/repo-migrate.test.e2e.js @@ -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(); - 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'); @@ -24,7 +24,7 @@ test('Migration Progress Page', async ({page: unauthedPage, browser}, workerInfo await form.locator('button.primary').click({timeout: 5000}); 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 page.reload(); diff --git a/tests/e2e/repo-settings.test.e2e.js b/tests/e2e/repo-settings.test.e2e.js index 226cc062d3..b4fdc1ae6c 100644 --- a/tests/e2e/repo-settings.test.e2e.js +++ b/tests/e2e/repo-settings.test.e2e.js @@ -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'); const page = await login({browser}, workerInfo); 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 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'); const page = await login({browser}, workerInfo); 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'); diff --git a/tests/e2e/shared/forms.js b/tests/e2e/shared/forms.js index 0ffd6eef2d..5775c40826 100644 --- a/tests/e2e/shared/forms.js +++ b/tests/e2e/shared/forms.js @@ -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 for (const l of await page.locator('label').all()) { 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 @@ -36,9 +36,9 @@ export async function validate_form({page}, scope) { const boxes = await Promise.all(helpLabels.map((help) => help.boundingBox())); for (let i = 1; i < boxes.length; i++) { // 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 - 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); } } } diff --git a/tests/e2e/utils_e2e.js b/tests/e2e/utils_e2e.js index 6afbe43b86..7cfd7388ca 100644 --- a/tests/e2e/utils_e2e.js +++ b/tests/e2e/utils_e2e.js @@ -30,7 +30,7 @@ export async function login_user(browser, workerInfo, user) { // Route to login page // Note: this could probably be done more quickly with a POST 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 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 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 await context.storageState({path: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`}); diff --git a/tests/e2e/webauthn.test.e2e.js b/tests/e2e/webauthn.test.e2e.js index 1f645ffea2..7168de223a 100644 --- a/tests/e2e/webauthn.test.e2e.js +++ b/tests/e2e/webauthn.test.e2e.js @@ -18,7 +18,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => // Register a security key. 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 const cdpSession = await page.context().newCDPSession(page); @@ -45,7 +45,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => // 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('Password').fill('password'); @@ -55,7 +55,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => // Cleanup. 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: 'Yes'}).click(); await page.waitForURL(`${workerInfo.project.use.baseURL}/user/settings/security`);