From 3a3416c0699db0e5ffc14cbdd94f081d2c1e36c0 Mon Sep 17 00:00:00 2001
From: Anbraten <anton@ju60.de>
Date: Fri, 8 Nov 2024 09:55:54 +0100
Subject: [PATCH] Migrate playwright to typescript

---
 playwright.config.ts                             |  6 +++---
 tests/e2e/README.md                              |  8 ++++----
 .../{actions.test.e2e.js => actions.test.e2e.ts} |  4 +---
 tests/e2e/changes.go                             |  2 +-
 ...st.e2e.js => dashboard-ci-status.test.e2e.ts} |  4 +---
 tests/e2e/e2e_test.go                            |  2 +-
 .../{example.test.e2e.js => example.test.e2e.ts} |  4 +---
 .../{explore.test.e2e.js => explore.test.e2e.ts} |  3 +--
 ...ent.test.e2e.js => issue-comment.test.e2e.ts} |  4 +---
 ...bar.test.e2e.js => issue-sidebar.test.e2e.ts} |  4 +---
 ...r.test.e2e.js => markdown-editor.test.e2e.ts} |  4 +---
 .../{markup.test.e2e.js => markup.test.e2e.ts}   |  4 +---
 ...ings.test.e2e.js => org-settings.test.e2e.ts} |  6 ++----
 ...s.test.e2e.js => profile_actions.test.e2e.ts} |  4 +---
 ...est.e2e.js => reaction-selectors.test.e2e.ts} |  4 +---
 .../{release.test.e2e.js => release.test.e2e.ts} |  6 ++----
 ...po-code.test.e2e.js => repo-code.test.e2e.ts} |  4 +---
 ....test.e2e.js => repo-commitgraph.test.e2e.ts} |  4 +---
 ...rate.test.e2e.js => repo-migrate.test.e2e.ts} |  4 +---
 ...ngs.test.e2e.js => repo-settings.test.e2e.ts} |  6 ++----
 ...po-wiki.test.e2e.js => repo-wiki.test.e2e.ts} |  4 +---
 ....e2e.js => right-settings-button.test.e2e.ts} |  4 +---
 tests/e2e/shared/{forms.js => forms.ts}          |  5 ++---
 tests/e2e/{utils_e2e.js => utils_e2e.ts}         | 16 ++++++++--------
 ...webauthn.test.e2e.js => webauthn.test.e2e.ts} |  5 ++---
 25 files changed, 42 insertions(+), 79 deletions(-)
 rename tests/e2e/{actions.test.e2e.js => actions.test.e2e.ts} (99%)
 rename tests/e2e/{dashboard-ci-status.test.e2e.js => dashboard-ci-status.test.e2e.ts} (97%)
 rename tests/e2e/{example.test.e2e.js => example.test.e2e.ts} (96%)
 rename tests/e2e/{explore.test.e2e.js => explore.test.e2e.ts} (96%)
 rename tests/e2e/{issue-comment.test.e2e.js => issue-comment.test.e2e.ts} (99%)
 rename tests/e2e/{issue-sidebar.test.e2e.js => issue-sidebar.test.e2e.ts} (99%)
 rename tests/e2e/{markdown-editor.test.e2e.js => markdown-editor.test.e2e.ts} (99%)
 rename tests/e2e/{markup.test.e2e.js => markup.test.e2e.ts} (92%)
 rename tests/e2e/{org-settings.test.e2e.js => org-settings.test.e2e.ts} (89%)
 rename tests/e2e/{profile_actions.test.e2e.js => profile_actions.test.e2e.ts} (98%)
 rename tests/e2e/{reaction-selectors.test.e2e.js => reaction-selectors.test.e2e.ts} (99%)
 rename tests/e2e/{release.test.e2e.js => release.test.e2e.ts} (97%)
 rename tests/e2e/{repo-code.test.e2e.js => repo-code.test.e2e.ts} (99%)
 rename tests/e2e/{repo-commitgraph.test.e2e.js => repo-commitgraph.test.e2e.ts} (98%)
 rename tests/e2e/{repo-migrate.test.e2e.js => repo-migrate.test.e2e.ts} (98%)
 rename tests/e2e/{repo-settings.test.e2e.js => repo-settings.test.e2e.ts} (95%)
 rename tests/e2e/{repo-wiki.test.e2e.js => repo-wiki.test.e2e.ts} (94%)
 rename tests/e2e/{right-settings-button.test.e2e.js => right-settings-button.test.e2e.ts} (99%)
 rename tests/e2e/shared/{forms.js => forms.ts} (92%)
 rename tests/e2e/{utils_e2e.js => utils_e2e.ts} (80%)
 rename tests/e2e/{webauthn.test.e2e.js => webauthn.test.e2e.ts} (94%)

diff --git a/playwright.config.ts b/playwright.config.ts
index 194f7f7d36..625dc3bc69 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -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;
diff --git a/tests/e2e/README.md b/tests/e2e/README.md
index 36b9a187c3..d20f2cf633 100644
--- a/tests/e2e/README.md
+++ b/tests/e2e/README.md
@@ -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,
@@ -145,7 +145,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
@@ -212,7 +212,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
 
diff --git a/tests/e2e/actions.test.e2e.js b/tests/e2e/actions.test.e2e.ts
similarity index 99%
rename from tests/e2e/actions.test.e2e.js
rename to tests/e2e/actions.test.e2e.ts
index 01ddb7b971..5e3ae7e3ff 100644
--- a/tests/e2e/actions.test.e2e.js
+++ b/tests/e2e/actions.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/changes.go b/tests/e2e/changes.go
index acc1a796a4..1a314727d6 100644
--- a/tests/e2e/changes.go
+++ b/tests/e2e/changes.go
@@ -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/*",
diff --git a/tests/e2e/dashboard-ci-status.test.e2e.js b/tests/e2e/dashboard-ci-status.test.e2e.ts
similarity index 97%
rename from tests/e2e/dashboard-ci-status.test.e2e.js
rename to tests/e2e/dashboard-ci-status.test.e2e.ts
index 289430055c..a5bdc7ade9 100644
--- a/tests/e2e/dashboard-ci-status.test.e2e.js
+++ b/tests/e2e/dashboard-ci-status.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go
index 3e1bcefd66..b8c89625c0 100644
--- a/tests/e2e/e2e_test.go
+++ b/tests/e2e/e2e_test.go
@@ -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)
diff --git a/tests/e2e/example.test.e2e.js b/tests/e2e/example.test.e2e.ts
similarity index 96%
rename from tests/e2e/example.test.e2e.js
rename to tests/e2e/example.test.e2e.ts
index a413a218c6..90fd9169a4 100644
--- a/tests/e2e/example.test.e2e.js
+++ b/tests/e2e/example.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/explore.test.e2e.js b/tests/e2e/explore.test.e2e.ts
similarity index 96%
rename from tests/e2e/explore.test.e2e.js
rename to tests/e2e/explore.test.e2e.ts
index eb0c723f36..44c9b21f58 100644
--- a/tests/e2e/explore.test.e2e.js
+++ b/tests/e2e/explore.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/issue-comment.test.e2e.js b/tests/e2e/issue-comment.test.e2e.ts
similarity index 99%
rename from tests/e2e/issue-comment.test.e2e.js
rename to tests/e2e/issue-comment.test.e2e.ts
index 8a1e48d75f..7b8326b832 100644
--- a/tests/e2e/issue-comment.test.e2e.js
+++ b/tests/e2e/issue-comment.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/issue-sidebar.test.e2e.js b/tests/e2e/issue-sidebar.test.e2e.ts
similarity index 99%
rename from tests/e2e/issue-sidebar.test.e2e.js
rename to tests/e2e/issue-sidebar.test.e2e.ts
index 8b5fa59331..31962bf3b4 100644
--- a/tests/e2e/issue-sidebar.test.e2e.js
+++ b/tests/e2e/issue-sidebar.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/markdown-editor.test.e2e.js b/tests/e2e/markdown-editor.test.e2e.ts
similarity index 99%
rename from tests/e2e/markdown-editor.test.e2e.js
rename to tests/e2e/markdown-editor.test.e2e.ts
index 36b35d7e8e..5db242bb36 100644
--- a/tests/e2e/markdown-editor.test.e2e.js
+++ b/tests/e2e/markdown-editor.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/markup.test.e2e.js b/tests/e2e/markup.test.e2e.ts
similarity index 92%
rename from tests/e2e/markup.test.e2e.js
rename to tests/e2e/markup.test.e2e.ts
index f7ec31709d..2cf32669e4 100644
--- a/tests/e2e/markup.test.e2e.js
+++ b/tests/e2e/markup.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/org-settings.test.e2e.js b/tests/e2e/org-settings.test.e2e.ts
similarity index 89%
rename from tests/e2e/org-settings.test.e2e.js
rename to tests/e2e/org-settings.test.e2e.ts
index 21f34c123d..b645d94161 100644
--- a/tests/e2e/org-settings.test.e2e.js
+++ b/tests/e2e/org-settings.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/profile_actions.test.e2e.js b/tests/e2e/profile_actions.test.e2e.ts
similarity index 98%
rename from tests/e2e/profile_actions.test.e2e.js
rename to tests/e2e/profile_actions.test.e2e.ts
index d168037041..efb4dd2f49 100644
--- a/tests/e2e/profile_actions.test.e2e.js
+++ b/tests/e2e/profile_actions.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/reaction-selectors.test.e2e.js b/tests/e2e/reaction-selectors.test.e2e.ts
similarity index 99%
rename from tests/e2e/reaction-selectors.test.e2e.js
rename to tests/e2e/reaction-selectors.test.e2e.ts
index 184f25fe18..a52b47e036 100644
--- a/tests/e2e/reaction-selectors.test.e2e.js
+++ b/tests/e2e/reaction-selectors.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/release.test.e2e.js b/tests/e2e/release.test.e2e.ts
similarity index 97%
rename from tests/e2e/release.test.e2e.js
rename to tests/e2e/release.test.e2e.ts
index 76c7ac0212..373f23dfa7 100644
--- a/tests/e2e/release.test.e2e.js
+++ b/tests/e2e/release.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/repo-code.test.e2e.js b/tests/e2e/repo-code.test.e2e.ts
similarity index 99%
rename from tests/e2e/repo-code.test.e2e.js
rename to tests/e2e/repo-code.test.e2e.ts
index fdb92762ff..5207a6389c 100644
--- a/tests/e2e/repo-code.test.e2e.js
+++ b/tests/e2e/repo-code.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/repo-commitgraph.test.e2e.js b/tests/e2e/repo-commitgraph.test.e2e.ts
similarity index 98%
rename from tests/e2e/repo-commitgraph.test.e2e.js
rename to tests/e2e/repo-commitgraph.test.e2e.ts
index f06c68a55d..2a6fb1e6d7 100644
--- a/tests/e2e/repo-commitgraph.test.e2e.js
+++ b/tests/e2e/repo-commitgraph.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/repo-migrate.test.e2e.js b/tests/e2e/repo-migrate.test.e2e.ts
similarity index 98%
rename from tests/e2e/repo-migrate.test.e2e.js
rename to tests/e2e/repo-migrate.test.e2e.ts
index 2ad4400340..c4d1604a6f 100644
--- a/tests/e2e/repo-migrate.test.e2e.js
+++ b/tests/e2e/repo-migrate.test.e2e.ts
@@ -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'));
 
diff --git a/tests/e2e/repo-settings.test.e2e.js b/tests/e2e/repo-settings.test.e2e.ts
similarity index 95%
rename from tests/e2e/repo-settings.test.e2e.js
rename to tests/e2e/repo-settings.test.e2e.ts
index b4fdc1ae6c..8bd7299182 100644
--- a/tests/e2e/repo-settings.test.e2e.js
+++ b/tests/e2e/repo-settings.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/repo-wiki.test.e2e.js b/tests/e2e/repo-wiki.test.e2e.ts
similarity index 94%
rename from tests/e2e/repo-wiki.test.e2e.js
rename to tests/e2e/repo-wiki.test.e2e.ts
index eb6c033748..5b681c583c 100644
--- a/tests/e2e/repo-wiki.test.e2e.js
+++ b/tests/e2e/repo-wiki.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/right-settings-button.test.e2e.js b/tests/e2e/right-settings-button.test.e2e.ts
similarity index 99%
rename from tests/e2e/right-settings-button.test.e2e.js
rename to tests/e2e/right-settings-button.test.e2e.ts
index 87e10c040c..bfb1800a27 100644
--- a/tests/e2e/right-settings-button.test.e2e.js
+++ b/tests/e2e/right-settings-button.test.e2e.ts
@@ -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');
diff --git a/tests/e2e/shared/forms.js b/tests/e2e/shared/forms.ts
similarity index 92%
rename from tests/e2e/shared/forms.js
rename to tests/e2e/shared/forms.ts
index 5775c40826..52432ccbe8 100644
--- a/tests/e2e/shared/forms.js
+++ b/tests/e2e/shared/forms.ts
@@ -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')
diff --git a/tests/e2e/utils_e2e.js b/tests/e2e/utils_e2e.ts
similarity index 80%
rename from tests/e2e/utils_e2e.js
rename to tests/e2e/utils_e2e.ts
index 7cfd7388ca..050847c5ed 100644
--- a/tests/e2e/utils_e2e.js
+++ b/tests/e2e/utils_e2e.ts
@@ -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: {
diff --git a/tests/e2e/webauthn.test.e2e.js b/tests/e2e/webauthn.test.e2e.ts
similarity index 94%
rename from tests/e2e/webauthn.test.e2e.js
rename to tests/e2e/webauthn.test.e2e.ts
index 7168de223a..38e6b27821 100644
--- a/tests/e2e/webauthn.test.e2e.js
+++ b/tests/e2e/webauthn.test.e2e.ts
@@ -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?!
     },
   });