Merge pull request 'refactor: Migrate playwright to typescript' (#5734) from anbraten/forgejo:ts-test into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5734
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
This commit is contained in:
Otto 2024-11-11 16:58:29 +00:00
commit 1b3497c9c4
25 changed files with 42 additions and 79 deletions

View file

@ -1,4 +1,4 @@
import {devices} from '@playwright/test';
import {devices, type PlaywrightTestConfig} from '@playwright/test';
const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
@ -8,7 +8,7 @@ const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localho
*/
export default {
testDir: './tests/e2e/',
testMatch: /.*\.test\.e2e\.js/, // Match any .test.e2e.js files
testMatch: /.*\.test\.e2e\.ts/, // Match any .test.e2e.js files
// you can adjust this value locally to match your machine's power,
// or pass `--workers x` to playwright
@ -99,4 +99,4 @@ export default {
outputDir: 'tests/e2e/test-artifacts/',
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
snapshotDir: 'tests/e2e/test-snapshots/',
};
} satisfies PlaywrightTestConfig;

View file

@ -77,7 +77,7 @@ and playwright to perform tests on it.
> (e.g. when only creating new content),
> or that they restore the initial state for the next browser run.
#### With the playwright UI:
#### With the playwright UI:
Playwright ships with an integrated UI mode which allows you to
run individual tests and to debug them by seeing detailed traces of what playwright does.
@ -90,7 +90,7 @@ npx playwright test --ui
#### Running individual tests
```
npx playwright test actions.test.e2e.js:9
npx playwright test actions.test.e2e.ts:9
```
First, specify the complete test filename,
@ -144,7 +144,7 @@ TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=test TEST_PGSQL_USERNAME=postgr
### Running individual tests
Example command to run `example.test.e2e.js` test file:
Example command to run `example.test.e2e.ts` test file:
> **Note**
> Unlike integration tests, this filtering is at the file level, not function
@ -211,7 +211,7 @@ Feel free to improve the logic used there if you need more advanced functionalit
If you can, perform automated accessibility testing using
[AxeCore](https://github.com/dequelabs/axe-core-npm/blob/develop/packages/playwright/README.md).
Take a look at `shared/forms.js` and some other places for inspiration.
Take a look at `shared/forms.ts` and some other places for inspiration.
### List related files coverage

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/repo/actions/**
// web_src/css/actions.css
@ -12,7 +10,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -29,7 +29,7 @@ func initChangedFiles() {
globalPatterns := []string{
// meta and config
"Makefile",
"playwright.config.js",
"playwright.config.ts",
".forgejo/workflows/testing.yml",
"tests/e2e/*.go",
"tests/e2e/shared/*",

View file

@ -1,11 +1,9 @@
// @ts-check
// @watch start
// web_src/js/components/DashboardRepoList.vue
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -79,7 +79,7 @@ func TestMain(m *testing.M) {
// TestE2e should be the only test e2e necessary. It will collect all "*.test.e2e.js" files in this directory and build a test for each.
func TestE2e(t *testing.T) {
// Find the paths of all e2e test files in test directory.
searchGlob := filepath.Join(filepath.Dir(setting.AppPath), "tests", "e2e", "*.test.e2e.js")
searchGlob := filepath.Join(filepath.Dir(setting.AppPath), "tests", "e2e", "*.test.e2e.ts")
paths, err := filepath.Glob(searchGlob)
if err != nil {
t.Fatal(err)

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/user/auth/**
// web_src/js/features/user-**
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, save_visual} from './utils_e2e.js';
import {test, login_user, save_visual} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,4 +1,3 @@
// @ts-check
// document is a global in evaluate, so it's safe to ignore here
// eslint playwright/no-conditional-in-test: 0
@ -8,7 +7,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test} from './utils_e2e.js';
import {test} from './utils_e2e.ts';
test('Explore view taborder', async ({page}) => {
await page.goto('/explore/repos');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// web_src/js/features/comp/**
// web_src/js/features/repo-**
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.js';
import {test, login_user, login} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/repo/issue/view_content/**
// web_src/css/repo/issue-**
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.js';
import {test, login_user, login} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// web_src/js/features/comp/ComboMarkdownEditor.js
// web_src/css/editor/combomarkdowneditor.css
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, load_logged_in_context, login_user} from './utils_e2e.js';
import {test, load_logged_in_context, login_user} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,11 +1,9 @@
// @ts-check
// @watch start
// web_src/css/markup/**
// @watch end
import {expect} from '@playwright/test';
import {test} from './utils_e2e.js';
import {test} from './utils_e2e.ts';
test('markup with #xyz-mode-only', async ({page}) => {
const response = await page.goto('/user2/repo1/issues/1');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/org/team/new.tmpl
// web_src/css/form.css
@ -7,8 +5,8 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.js';
import {validate_form} from './shared/forms.js';
import {test, login_user, login} from './utils_e2e.ts';
import {validate_form} from './shared/forms.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// routers/web/user/**
// templates/shared/user/**
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test('Follow actions', async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,12 +1,10 @@
// @ts-check
// @watch start
// web_src/js/features/comp/ReactionSelector.js
// routers/web/repo/issue.go
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// models/repo/attachment.go
// modules/structs/attachment.go
@ -11,8 +9,8 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.js';
import {validate_form} from './shared/forms.js';
import {test, login_user, save_visual, load_logged_in_context} from './utils_e2e.ts';
import {validate_form} from './shared/forms.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// web_src/js/features/repo-code.js
// web_src/css/repo.css
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/repo/graph.tmpl
// web_src/css/features/gitgraph.css
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,11 +1,9 @@
// @ts-check
// @watch start
// web_src/js/features/repo-migrate.js
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(({browser}, workerInfo) => login_user(browser, workerInfo, 'user2'));

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/webhook/shared-settings.tmpl
// templates/repo/settings/**
@ -9,8 +7,8 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, login} from './utils_e2e.js';
import {validate_form} from './shared/forms.js';
import {test, login_user, login} from './utils_e2e.ts';
import {validate_form} from './shared/forms.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,12 +1,10 @@
// @ts-check
// @watch start
// templates/repo/wiki/**
// web_src/css/repo**
// @watch end
import {expect} from '@playwright/test';
import {test} from './utils_e2e.js';
import {test} from './utils_e2e.ts';
test(`Search for long titles and test for no overflow`, async ({page}, workerInfo) => {
test.skip(workerInfo.project.name === 'Mobile Safari', 'Fails as always, see https://codeberg.org/forgejo/forgejo/pulls/5326#issuecomment-2313275');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/org/**
// templates/repo/**
@ -7,7 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
import {test, login_user, load_logged_in_context} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');

View file

@ -1,8 +1,7 @@
import {expect} from '@playwright/test';
import {expect, type Page} from '@playwright/test';
import {AxeBuilder} from '@axe-core/playwright';
export async function validate_form({page}, scope) {
scope ??= 'form';
export async function validate_form({page}: {page: Page}, scope: 'form' | 'fieldset' = 'form') {
const accessibilityScanResults = await new AxeBuilder({page})
// disable checking for link style - should be fixed, but not now
.disableRules('link-in-text-block')

View file

@ -1,4 +1,4 @@
import {expect, test as baseTest} 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({
context: async ({browser}, use) => {
@ -6,7 +6,7 @@ export const test = baseTest.extend({
},
});
async function test_context(browser, options) {
async function test_context(browser: Browser, options?: BrowserContextOptions) {
const context = await browser.newContext(options);
context.on('page', (page) => {
@ -21,7 +21,7 @@ const LOGIN_PASSWORD = 'password';
// log in user and store session info. This should generally be
// run in test.beforeAll(), then the session can be loaded in tests.
export async function login_user(browser, workerInfo, user) {
export async function login_user(browser: Browser, workerInfo: TestInfo, user: string) {
test.setTimeout(60000);
// Set up a new context
const context = await test_context(browser);
@ -47,7 +47,7 @@ export async function login_user(browser, workerInfo, user) {
return context;
}
export async function load_logged_in_context(browser, workerInfo, user) {
export async function load_logged_in_context(browser: Browser, workerInfo: TestInfo, user: string) {
let context;
try {
context = await test_context(browser, {storageState: `${ARTIFACTS_PATH}/state-${user}-${workerInfo.workerIndex}.json`});
@ -59,12 +59,12 @@ export async function load_logged_in_context(browser, workerInfo, user) {
return context;
}
export async function login({browser}, workerInfo) {
export async function login({browser}: {browser: Browser}, workerInfo: TestInfo) {
const context = await load_logged_in_context(browser, workerInfo, 'user2');
return await context.newPage();
return await context?.newPage();
}
export async function save_visual(page) {
export async function save_visual(page: Page) {
// Optionally include visual testing
if (process.env.VISUAL_TEST) {
await page.waitForLoadState('networkidle');
@ -83,7 +83,7 @@ export async function save_visual(page) {
// Create a temporary user and login to that user and store session info.
// This should ideally run on a per test basis.
export async function create_temp_user(browser, workerInfo, request) {
export async function create_temp_user(browser: Browser, workerInfo: TestInfo, request: APIRequestContext) {
const username = globalThis.crypto.randomUUID();
const newUser = await request.post(`/api/v1/admin/users`, {
headers: {

View file

@ -1,6 +1,5 @@
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// @ts-check
// @watch start
// templates/user/auth/**
@ -9,7 +8,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, create_temp_user} from './utils_e2e.js';
import {test, create_temp_user} from './utils_e2e.ts';
test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => {
test.skip(workerInfo.project.name !== 'chromium', 'Uses Chrome protocol');
@ -31,7 +30,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
transport: 'usb',
automaticPresenceSimulation: true,
isUserVerified: true,
backupEligibility: true,
backupEligibility: true, // TODO: this doesn't seem to be available?!
},
});