Template
1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo synced 2024-11-25 03:06:10 +01:00

Merge remote-tracking branch 'upstream/forgejo' into moremarkdown

This commit is contained in:
JakobDev 2024-11-13 17:54:50 +01:00
commit 1898f3d82a
34 changed files with 454 additions and 343 deletions

View file

@ -59,6 +59,24 @@ jobs:
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
verbose: ${{ vars.VERBOSE }}
- name: get trigger mirror issue
id: mirror
uses: https://code.forgejo.org/infrastructure/issue-action/get@v1.1.0
with:
forgejo: https://code.forgejo.org
repository: forgejo/forgejo
labels: mirror-trigger
- name: trigger the mirror
uses: https://code.forgejo.org/infrastructure/issue-action/set@v1.1.0
with:
forgejo: https://code.forgejo.org
repository: forgejo/forgejo
token: ${{ secrets.LABEL_ISSUE_FORGEJO_MIRROR_TOKEN }}
numbers: ${{ steps.mirror.outputs.numbers }}
label-wait-if-exists: 3600
label: trigger
- name: upgrade v*.next.forgejo.org
uses: https://code.forgejo.org/infrastructure/next-digest@v1.1.0
with:

View file

@ -716,7 +716,6 @@ test-e2e-pgsql\#%: playwright e2e.pgsql.test generate-ini-pgsql
.PHONY: test-e2e-debugserver
test-e2e-debugserver: e2e.sqlite.test generate-ini-sqlite
sed -i s/3003/3000/g tests/sqlite.ini
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestDebugserver -test.timeout 24h
.PHONY: bench-sqlite

View file

@ -20,7 +20,7 @@ export default tseslint.config(
...tseslint.configs.recommended,
eslintPluginImportX.flatConfigs.typescript,
{
ignores: ['web_src/js/vendor', 'web_src/fomantic', 'public/assets/js'],
ignores: ['web_src/js/vendor', 'web_src/fomantic', 'public/assets/js', 'tests/e2e/reports/'],
},
{
plugins: {
@ -1112,7 +1112,7 @@ export default tseslint.config(
],
},
}, {
files: ['tests/e2e/**/*.js', 'tests/e2e/**/*.ts'],
files: ['tests/e2e/**/*.ts'],
languageOptions: {
globals: {
...globals.browser,
@ -1125,7 +1125,8 @@ export default tseslint.config(
...playwright.configs['flat/recommended'].rules,
'playwright/no-conditional-in-test': [0],
'playwright/no-conditional-expect': [0],
'playwright/no-networkidle': [0],
// allow grouping helper functions with tests
'unicorn/consistent-function-scoping': [0],
'playwright/no-skipped-test': [
2,

2
go.mod
View file

@ -24,7 +24,7 @@ require (
github.com/alecthomas/chroma/v2 v2.14.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.4.2
github.com/buildkite/terminal-to-html/v3 v3.16.3
github.com/buildkite/terminal-to-html/v3 v3.16.4
github.com/caddyserver/certmagic v0.21.4
github.com/chi-middleware/proxy v1.1.1
github.com/djherbis/buffer v1.2.0

4
go.sum
View file

@ -143,8 +143,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk=
github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
github.com/buildkite/terminal-to-html/v3 v3.16.4 h1:QFYO8IGvRnp7tGgiQb8g9uFU8kY9wOzxsFFx17+yy6Q=
github.com/buildkite/terminal-to-html/v3 v3.16.4/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=

269
package-lock.json generated
View file

@ -37,7 +37,7 @@
"monaco-editor": "0.51.0",
"monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0",
"postcss": "8.4.48",
"postcss": "8.4.49",
"postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1",
"pretty-ms": "9.0.0",
@ -60,21 +60,21 @@
"wrap-ansi": "9.0.0"
},
"devDependencies": {
"@axe-core/playwright": "4.10.0",
"@axe-core/playwright": "4.10.1",
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
"@playwright/test": "1.48.2",
"@stoplight/spectral-cli": "6.13.1",
"@stoplight/spectral-cli": "6.14.0",
"@stylistic/eslint-plugin-js": "2.10.1",
"@stylistic/stylelint-plugin": "3.1.1",
"@typescript-eslint/parser": "8.13.0",
"@typescript-eslint/parser": "8.14.0",
"@vitejs/plugin-vue": "5.1.5",
"@vitest/coverage-v8": "2.1.4",
"@vitest/eslint-plugin": "1.1.7",
"@vitest/eslint-plugin": "1.1.10",
"@vue/test-utils": "2.4.6",
"eslint": "9.14.0",
"eslint-import-resolver-typescript": "3.6.3",
"eslint-plugin-array-func": "5.0.2",
"eslint-plugin-import-x": "4.4.0",
"eslint-plugin-import-x": "4.4.2",
"eslint-plugin-no-jquery": "3.0.2",
"eslint-plugin-no-use-extend-native": "0.7.2",
"eslint-plugin-playwright": "2.0.1",
@ -82,11 +82,11 @@
"eslint-plugin-sonarjs": "2.0.4",
"eslint-plugin-unicorn": "56.0.0",
"eslint-plugin-vitest-globals": "1.5.0",
"eslint-plugin-vue": "9.30.0",
"eslint-plugin-vue": "9.31.0",
"eslint-plugin-vue-scoped-css": "2.8.1",
"eslint-plugin-wc": "2.2.0",
"globals": "15.12.0",
"happy-dom": "15.11.0",
"happy-dom": "15.11.4",
"license-checker-rseidelsohn": "4.4.2",
"markdownlint-cli": "0.42.0",
"postcss-html": "1.7.0",
@ -96,7 +96,7 @@
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.2.0",
"typescript": "5.6.3",
"typescript-eslint": "8.13.0",
"typescript-eslint": "8.14.0",
"vite-string-plugin": "1.3.4",
"vitest": "2.1.4"
},
@ -163,13 +163,13 @@
}
},
"node_modules/@axe-core/playwright": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.0.tgz",
"integrity": "sha512-kEr3JPEVUSnKIYp/egV2jvFj+chIjCjPp3K3zlpJMza/CB3TFw8UZNbI9agEC2uMz4YbgAOyzlbUy0QS+OofFA==",
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.1.tgz",
"integrity": "sha512-EV5t39VV68kuAfMKqb/RL+YjYKhfuGim9rgIaQ6Vntb2HgaCaau0h98Y3WEUqW1+PbdzxDtDNjFAipbtZuBmEA==",
"dev": true,
"license": "MPL-2.0",
"dependencies": {
"axe-core": "~4.10.0"
"axe-core": "~4.10.2"
},
"peerDependencies": {
"playwright-core": ">= 1.0.0"
@ -3351,10 +3351,23 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@jsep-plugin/assignment": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz",
"integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10.16.0"
},
"peerDependencies": {
"jsep": "^0.4.0||^1.0.0"
}
},
"node_modules/@jsep-plugin/regex": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.3.tgz",
"integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz",
"integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==",
"dev": true,
"license": "MIT",
"engines": {
@ -3365,9 +3378,9 @@
}
},
"node_modules/@jsep-plugin/ternary": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.3.tgz",
"integrity": "sha512-qtLGzCNzPVJ3kdH6/zoLWDPjauHIKiLSBAR71Wa0+PWvGA8wODUQvRgxtpUA5YqAYL3CQ8S4qXhd/9WuWTZirg==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.4.tgz",
"integrity": "sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==",
"dev": true,
"license": "MIT",
"engines": {
@ -3992,20 +4005,20 @@
}
},
"node_modules/@stoplight/spectral-cli": {
"version": "6.13.1",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.13.1.tgz",
"integrity": "sha512-v6ipX4w6wRhtbOotwdPL7RrEkP0m1OwHTIyqzVrAPi932F/zkee24jmf1CHNrTynonmfGoU6/XpeqUHtQdKDFw==",
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.14.0.tgz",
"integrity": "sha512-pq1qWENLtI97afz9Ygx0TtBXj9s97oQnjMOUp4USzXdnxhKhlYwlhJIA9U3VYzktA+QpHdTXVd4GSgyxdHSBSg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@stoplight/json": "~3.21.0",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-core": "^1.18.3",
"@stoplight/spectral-formatters": "^1.3.0",
"@stoplight/spectral-parsers": "^1.0.3",
"@stoplight/spectral-core": "^1.19.2",
"@stoplight/spectral-formatters": "^1.4.0",
"@stoplight/spectral-parsers": "^1.0.4",
"@stoplight/spectral-ref-resolver": "^1.0.4",
"@stoplight/spectral-ruleset-bundler": "^1.5.4",
"@stoplight/spectral-ruleset-migrator": "^1.9.6",
"@stoplight/spectral-ruleset-bundler": "^1.6.0",
"@stoplight/spectral-ruleset-migrator": "^1.11.0",
"@stoplight/spectral-rulesets": ">=1",
"@stoplight/spectral-runtime": "^1.1.2",
"@stoplight/types": "^13.6.0",
@ -4022,7 +4035,7 @@
"spectral": "dist/index.js"
},
"engines": {
"node": "^12.20 || >= 14.13"
"node": "^16.20 || ^18.18 || >= 20.17"
}
},
"node_modules/@stoplight/spectral-cli/node_modules/fast-glob": {
@ -4056,9 +4069,9 @@
}
},
"node_modules/@stoplight/spectral-core": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.19.1.tgz",
"integrity": "sha512-YiWhXdjyjn4vCl3102ywzwCEJzncxapFcj4dxcj1YP/bZ62DFeGJ8cEaMP164vSw2kI3rX7EMMzI/c8XOUnTfQ==",
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.19.2.tgz",
"integrity": "sha512-Yx1j7d0VGEbsOCimPgl+L8w7ZuuOaxqGvXSUXgm9weoGR5idLQjPaTuHLdfdziR1gjqQdVTCEk/dN0cFfUKhow==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -4075,17 +4088,17 @@
"ajv-errors": "~3.0.0",
"ajv-formats": "~2.1.0",
"es-aggregate-error": "^1.0.7",
"jsonpath-plus": "7.1.0",
"jsonpath-plus": "10.1.0",
"lodash": "~4.17.21",
"lodash.topath": "^4.5.2",
"minimatch": "3.1.2",
"nimma": "0.2.2",
"nimma": "0.2.3",
"pony-cause": "^1.0.0",
"simple-eval": "1.0.0",
"simple-eval": "1.0.1",
"tslib": "^2.3.0"
},
"engines": {
"node": "^12.20 || >= 14.13"
"node": "^16.20 || ^18.18 || >= 20.17"
}
},
"node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": {
@ -4266,9 +4279,9 @@
}
},
"node_modules/@stoplight/spectral-ruleset-migrator": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.10.0.tgz",
"integrity": "sha512-nDfkVfYeWWv0UvILC4TWZSnRqQ4rHgeOJO1/lHQ7XHeG5iONanQ639B1aK6ZS6vuUc8gwuyQsrPF67b4sHIyYw==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.11.0.tgz",
"integrity": "sha512-FHxc/C/RhEYXW8zcp9mO50/jt+0Of6p6ZFVoV84l9y7agQchc9RGFjN6src4kO7bg6eUWQK6+5rUIV6yFKhBgg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -4282,13 +4295,13 @@
"@types/node": "*",
"ajv": "^8.17.1",
"ast-types": "0.14.2",
"astring": "^1.7.5",
"astring": "^1.9.0",
"reserved": "0.1.2",
"tslib": "^2.3.1",
"validate-npm-package-name": "3.0.0"
},
"engines": {
"node": ">=12"
"node": "^16.20 || ^18.18 || >= 20.17"
}
},
"node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/yaml": {
@ -4866,17 +4879,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz",
"integrity": "sha512-nQtBLiZYMUPkclSeC3id+x4uVd1SGtHuElTxL++SfP47jR0zfkZBJHc+gL4qPsgTuypz0k8Y2GheaDYn6Gy3rg==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.14.0.tgz",
"integrity": "sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.13.0",
"@typescript-eslint/type-utils": "8.13.0",
"@typescript-eslint/utils": "8.13.0",
"@typescript-eslint/visitor-keys": "8.13.0",
"@typescript-eslint/scope-manager": "8.14.0",
"@typescript-eslint/type-utils": "8.14.0",
"@typescript-eslint/utils": "8.14.0",
"@typescript-eslint/visitor-keys": "8.14.0",
"graphemer": "^1.4.0",
"ignore": "^5.3.1",
"natural-compare": "^1.4.0",
@ -4900,16 +4913,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.13.0.tgz",
"integrity": "sha512-w0xp+xGg8u/nONcGw1UXAr6cjCPU1w0XVyBs6Zqaj5eLmxkKQAByTdV/uGgNN5tVvN/kKpoQlP2cL7R+ajZZIQ==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.14.0.tgz",
"integrity": "sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/scope-manager": "8.13.0",
"@typescript-eslint/types": "8.13.0",
"@typescript-eslint/typescript-estree": "8.13.0",
"@typescript-eslint/visitor-keys": "8.13.0",
"@typescript-eslint/scope-manager": "8.14.0",
"@typescript-eslint/types": "8.14.0",
"@typescript-eslint/typescript-estree": "8.14.0",
"@typescript-eslint/visitor-keys": "8.14.0",
"debug": "^4.3.4"
},
"engines": {
@ -4929,14 +4942,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.13.0.tgz",
"integrity": "sha512-XsGWww0odcUT0gJoBZ1DeulY1+jkaHUciUq4jKNv4cpInbvvrtDoyBH9rE/n2V29wQJPk8iCH1wipra9BhmiMA==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz",
"integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.13.0",
"@typescript-eslint/visitor-keys": "8.13.0"
"@typescript-eslint/types": "8.14.0",
"@typescript-eslint/visitor-keys": "8.14.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -4947,14 +4960,14 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.13.0.tgz",
"integrity": "sha512-Rqnn6xXTR316fP4D2pohZenJnp+NwQ1mo7/JM+J1LWZENSLkJI8ID8QNtlvFeb0HnFSK94D6q0cnMX6SbE5/vA==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.14.0.tgz",
"integrity": "sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/typescript-estree": "8.13.0",
"@typescript-eslint/utils": "8.13.0",
"@typescript-eslint/typescript-estree": "8.14.0",
"@typescript-eslint/utils": "8.14.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.3.0"
},
@ -4972,9 +4985,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.13.0.tgz",
"integrity": "sha512-4cyFErJetFLckcThRUFdReWJjVsPCqyBlJTi6IDEpc1GWCIIZRFxVppjWLIMcQhNGhdWJJRYFHpHoDWvMlDzng==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz",
"integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==",
"dev": true,
"license": "MIT",
"engines": {
@ -4986,14 +4999,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.13.0.tgz",
"integrity": "sha512-v7SCIGmVsRK2Cy/LTLGN22uea6SaUIlpBcO/gnMGT/7zPtxp90bphcGf4fyrCQl3ZtiBKqVTG32hb668oIYy1g==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz",
"integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/types": "8.13.0",
"@typescript-eslint/visitor-keys": "8.13.0",
"@typescript-eslint/types": "8.14.0",
"@typescript-eslint/visitor-keys": "8.14.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@ -5031,16 +5044,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.13.0.tgz",
"integrity": "sha512-A1EeYOND6Uv250nybnLZapeXpYMl8tkzYUxqmoKAWnI4sei3ihf2XdZVd+vVOmHGcp3t+P7yRrNsyyiXTvShFQ==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz",
"integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "8.13.0",
"@typescript-eslint/types": "8.13.0",
"@typescript-eslint/typescript-estree": "8.13.0"
"@typescript-eslint/scope-manager": "8.14.0",
"@typescript-eslint/types": "8.14.0",
"@typescript-eslint/typescript-estree": "8.14.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -5054,13 +5067,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.13.0.tgz",
"integrity": "sha512-7N/+lztJqH4Mrf0lb10R/CbI1EaAMMGyF5y0oJvFoAhafwgiRA7TXyd8TFn8FC8k5y2dTsYogg238qavRGNnlw==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz",
"integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.13.0",
"@typescript-eslint/types": "8.14.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
@ -5142,9 +5155,9 @@
}
},
"node_modules/@vitest/eslint-plugin": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.7.tgz",
"integrity": "sha512-pTWGW3y6lH2ukCuuffpan6kFxG6nIuoesbhMiQxskyQMRcCN5t9SXsKrNHvEw3p8wcCsgJoRqFZVkOTn6TjclA==",
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.10.tgz",
"integrity": "sha512-uScH5Kz5v32vvtQYB2iodpoPg2mGASK+VKpjlc2IUgE0+16uZKqVKi2vQxjxJ6sMCQLBs4xhBFZlmZBszsmfKQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@ -8433,9 +8446,9 @@
}
},
"node_modules/eslint-plugin-import-x": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.4.0.tgz",
"integrity": "sha512-me58aWTjdkPtgmOzPe+uP0bebpN5etH4bJRnYzy85Rn9g/3QyASg6kTCqdwNzyaJRqMI2ii2o8s01P2LZpREHg==",
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.4.2.tgz",
"integrity": "sha512-mDRXPSLQ0UQZQw91QdG4/qZT6hgeW2MJTczAbgPseUZuPEtIjjdPOolXroRkulnOn3fzj6gNgvk+wchMJiHElg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -9365,9 +9378,9 @@
"license": "MIT"
},
"node_modules/eslint-plugin-vue": {
"version": "9.30.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.30.0.tgz",
"integrity": "sha512-CyqlRgShvljFkOeYK8wN5frh/OGTvkj1S7wlr2Q2pUvwq+X5VYiLd6ZjujpgSgLnys2W8qrBLkXQ41SUYaoPIQ==",
"version": "9.31.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.31.0.tgz",
"integrity": "sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -10257,9 +10270,9 @@
}
},
"node_modules/happy-dom": {
"version": "15.11.0",
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.0.tgz",
"integrity": "sha512-/zyxHbXriYJ8b9Urh43ILk/jd9tC07djURnJuAimJ3tJCOLOzOUp7dEHDwJOZyzROlrrooUhr/0INZIDBj1Bjw==",
"version": "15.11.4",
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.11.4.tgz",
"integrity": "sha512-AU6tzh3ADd28vSmXahgLsGyGGihXPGeKH0owDn9lhHolB6vIwEhag//T+TBzDoAcHhmVEwlxwSgtW1KZep+1MA==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -11417,9 +11430,9 @@
}
},
"node_modules/jsep": {
"version": "1.3.9",
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.9.tgz",
"integrity": "sha512-i1rBX5N7VPl0eYb6+mHNp52sEuaS2Wi8CDYx1X5sn9naevL78+265XJqy1qENEk7mRKwS06NHpUqiBwR7qeodw==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz",
"integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==",
"dev": true,
"license": "MIT",
"engines": {
@ -11498,13 +11511,22 @@
}
},
"node_modules/jsonpath-plus": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.1.0.tgz",
"integrity": "sha512-gTaNRsPWO/K2KY6MrqaUFClF9kmuM6MFH5Dhg1VYDODgFbByw1yb7xu3hrViE/sz+dGOeMWgCzwUwQtAnCTE9g==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.1.0.tgz",
"integrity": "sha512-gHfV1IYqH8uJHYVTs8BJX1XKy2/rR93+f8QQi0xhx95aCiXn1ettYAd5T+7FU6wfqyDoX/wy0pm/fL3jOKJ9Lg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jsep-plugin/assignment": "^1.2.1",
"@jsep-plugin/regex": "^1.0.3",
"jsep": "^1.3.9"
},
"bin": {
"jsonpath": "bin/jsonpath-cli.js",
"jsonpath-plus": "bin/jsonpath-cli.js"
},
"engines": {
"node": ">=12.0.0"
"node": ">=18.0.0"
}
},
"node_modules/jsonpointer": {
@ -12388,9 +12410,9 @@
"license": "MIT"
},
"node_modules/nimma": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.2.tgz",
"integrity": "sha512-V52MLl7BU+tH2Np9tDrIXK8bql3MVUadnMIl/0/oZSGC9keuro0O9UUv9QKp0aMvtN8HRew4G7byY7H4eWsxaQ==",
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/nimma/-/nimma-0.2.3.tgz",
"integrity": "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@ -12403,21 +12425,10 @@
"node": "^12.20 || >=14.13"
},
"optionalDependencies": {
"jsonpath-plus": "^6.0.1",
"jsonpath-plus": "^6.0.1 || ^10.1.0",
"lodash.topath": "^4.5.2"
}
},
"node_modules/nimma/node_modules/jsonpath-plus": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
"integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/node-fetch": {
"version": "2.6.13",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.13.tgz",
@ -13083,9 +13094,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.48",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.48.tgz",
"integrity": "sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==",
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [
{
"type": "opencollective",
@ -14487,13 +14498,13 @@
}
},
"node_modules/simple-eval": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.0.tgz",
"integrity": "sha512-kpKJR+bqTscgC0xuAl2xHN6bB12lHjC2DCUfqjAx19bQyO3R2EVLOurm3H9AUltv/uFVcSCVNc6faegR+8NYLw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-eval/-/simple-eval-1.0.1.tgz",
"integrity": "sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"jsep": "^1.1.2"
"jsep": "^1.3.6"
},
"engines": {
"node": ">=12"
@ -15893,15 +15904,15 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.13.0.tgz",
"integrity": "sha512-vIMpDRJrQd70au2G8w34mPps0ezFSPMEX4pXkTzUkrNbRX+36ais2ksGWN0esZL+ZMaFJEneOBHzCgSqle7DHw==",
"version": "8.14.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.14.0.tgz",
"integrity": "sha512-K8fBJHxVL3kxMmwByvz8hNdBJ8a0YqKzKDX6jRlrjMuNXyd5T2V02HIq37+OiWXvUUOXgOOGiSSOh26Mh8pC3w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.13.0",
"@typescript-eslint/parser": "8.13.0",
"@typescript-eslint/utils": "8.13.0"
"@typescript-eslint/eslint-plugin": "8.14.0",
"@typescript-eslint/parser": "8.14.0",
"@typescript-eslint/utils": "8.14.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"

View file

@ -36,7 +36,7 @@
"monaco-editor": "0.51.0",
"monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0",
"postcss": "8.4.48",
"postcss": "8.4.49",
"postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1",
"pretty-ms": "9.0.0",
@ -59,21 +59,21 @@
"wrap-ansi": "9.0.0"
},
"devDependencies": {
"@axe-core/playwright": "4.10.0",
"@axe-core/playwright": "4.10.1",
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
"@playwright/test": "1.48.2",
"@stoplight/spectral-cli": "6.13.1",
"@stoplight/spectral-cli": "6.14.0",
"@stylistic/eslint-plugin-js": "2.10.1",
"@stylistic/stylelint-plugin": "3.1.1",
"@typescript-eslint/parser": "8.13.0",
"@typescript-eslint/parser": "8.14.0",
"@vitejs/plugin-vue": "5.1.5",
"@vitest/coverage-v8": "2.1.4",
"@vitest/eslint-plugin": "1.1.7",
"@vitest/eslint-plugin": "1.1.10",
"@vue/test-utils": "2.4.6",
"eslint": "9.14.0",
"eslint-import-resolver-typescript": "3.6.3",
"eslint-plugin-array-func": "5.0.2",
"eslint-plugin-import-x": "4.4.0",
"eslint-plugin-import-x": "4.4.2",
"eslint-plugin-no-jquery": "3.0.2",
"eslint-plugin-no-use-extend-native": "0.7.2",
"eslint-plugin-playwright": "2.0.1",
@ -81,11 +81,11 @@
"eslint-plugin-sonarjs": "2.0.4",
"eslint-plugin-unicorn": "56.0.0",
"eslint-plugin-vitest-globals": "1.5.0",
"eslint-plugin-vue": "9.30.0",
"eslint-plugin-vue": "9.31.0",
"eslint-plugin-vue-scoped-css": "2.8.1",
"eslint-plugin-wc": "2.2.0",
"globals": "15.12.0",
"happy-dom": "15.11.0",
"happy-dom": "15.11.4",
"license-checker-rseidelsohn": "4.4.2",
"markdownlint-cli": "0.42.0",
"postcss-html": "1.7.0",
@ -95,7 +95,7 @@
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.2.0",
"typescript": "5.6.3",
"typescript-eslint": "8.13.0",
"typescript-eslint": "8.14.0",
"vite-string-plugin": "1.3.4",
"vitest": "2.1.4"
},

View file

@ -1,6 +1,6 @@
import {devices} from '@playwright/test';
import {devices, type PlaywrightTestConfig} from '@playwright/test';
const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3003';
/**
* @see https://playwright.dev/docs/test-configuration
@ -8,11 +8,11 @@ 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
workers: process.env.CI ? 1 : 2,
workers: 1,
/* Maximum time one test can run for. */
timeout: 30 * 1000,
@ -22,7 +22,7 @@ export default {
* Maximum time expect() should wait for the condition to be met.
* For example in `await expect(locator).toHaveText();`
*/
timeout: 2000,
timeout: 3000,
},
/* Fail the build on CI if you accidentally left test.only in the source code. */
@ -30,6 +30,8 @@ export default {
/* Retry on CI only */
retries: process.env.CI ? 1 : 0,
/* fail fast */
maxFailures: process.env.CI ? 1 : 0,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
@ -41,7 +43,7 @@ export default {
locale: 'en-US',
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
actionTimeout: 2000,
actionTimeout: 3000,
/* Maximum time allowed for navigation, such as `page.goto()`. */
navigationTimeout: 10 * 1000,
@ -95,8 +97,8 @@ export default {
},
],
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
/* Folder for test artifacts created during test execution such as screenshots, traces, etc. */
outputDir: 'tests/e2e/test-artifacts/',
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
/* Folder for explicit snapshots for visual testing */
snapshotDir: 'tests/e2e/test-snapshots/',
};
} satisfies PlaywrightTestConfig;

View file

@ -16,7 +16,7 @@
"prConcurrentLimit": 10,
"osvVulnerabilityAlerts": true,
"automergeStrategy": "squash",
"labels": ["dependency-upgrade"],
"labels": ["dependency-upgrade","test/not-needed"],
"packageRules": [
{
"description": "Require approval for python minor version",
@ -144,8 +144,9 @@
},
{
"description": "Automerge some packages when CI succeeds",
"extends": ["packages:linters", "packages:test"],
"extends": ["packages:linters", "packages:test", "schedule:monthly"],
"matchPackageNames": [
"@axe-core/playwright",
"@eslint-community/**",
"@playwright/**",
"@stoplight/spectral-cli",

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
@ -175,6 +175,70 @@ ACCEPT_VISUAL=1 will overwrite the snapshot images with new images.
If you know noteworthy tests that can act as an inspiration for new tests,
please add some details here.
### Understanding and waiting for page loads
[Waiting for a load state](https://playwright.dev/docs/api/class-frame#frame-wait-for-load-state)
sound like a convenient way to ensure the page was loaded,
but it only works once and consecutive calls to it
(e.g. after clicking a button which should reload a page)
return immediately without waiting for *another* load event.
If you match something which is on both the old and the new page,
you might succeed before the page was reloaded,
although the code using a `waitForLoadState` might intuitively suggest
the page was changed before.
Interacting with the page before the reload
(e.g. by opening a dropdown)
might then race and result in flaky tests,
depending on the speed of the hardware running the test.
A possible way to test that an interaction worked is by checking for a known change first.
For example:
- you submit a form and you want to check that the content persisted
- checking for the content directly would succeed even without a page reload
- check for a success message first (will wait until it appears), then verify the content
Alternatively, if you know the backend request that will be made before the reload,
you can explicitly wait for it:
~~~js
const submitted = page.waitForResponse('/my/backend/post/request');
await page.locator('button').first().click(); // perform your interaction
await submitted;
~~~
If the page redirects to another URL,
you can alternatively use:
~~~js
await page.waitForURL('**/target.html');
~~~
### Only sign in if necessary
Signing in takes time and is actually executed step-by-step.
If your test does not rely on a user account, skip this step.
~~~js
test('For anyone', async ({page}) => {
await page.goto('/somepage');
~~~
If you need a user account, you can use something like:
~~~js
import {test, login_user, login} from './utils_e2e.ts';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2'); // or another user
});
test('For signed users only', async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
~~~
### Run tests very selectively
Browser testing can take some time.
@ -211,7 +275,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
@ -264,3 +328,27 @@ and a set of files with a certain ending:
The patterns are evaluated on a "first-match" basis.
Under the hood, [gobwas/glob](https://github.com/gobwas/glob) is used.
## Grouped retry for interactions
Sometimes, it can be necessary to retry certain interactions together.
Consider the following procedure:
1. click to open a dropdown
2. interact with content in the dropdown
When for some reason the dropdown does not open,
for example because of it taking time to initialize after page load,
the click will succeed,
but the depending interaction won't,
although playwright repeatedly tries to find the content.
You can [group statements using toPass]()https://playwright.dev/docs/test-assertions#expecttopass).
This code retries the dropdown click until the second item is found.
~~~js
await expect(async () => {
await page.locator('.dropdown').click();
await page.locator('.dropdown .item').first().click();
}).toPass();
~~~

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');
@ -46,7 +44,6 @@ test('workflow dispatch error: missing inputs', async ({browser}, workerInfo) =>
const page = await context.newPage();
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
await page.waitForLoadState('networkidle');
await page.locator('#workflow_dispatch_dropdown>button').click();
@ -57,7 +54,6 @@ test('workflow dispatch error: missing inputs', async ({browser}, workerInfo) =>
});
await page.locator('#workflow-dispatch-submit').click();
await page.waitForLoadState('networkidle');
await expect(page.getByText('Require value for input "String w/o. default".')).toBeVisible();
});
@ -70,13 +66,11 @@ test('workflow dispatch success', async ({browser}, workerInfo) => {
const page = await context.newPage();
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
await page.waitForLoadState('networkidle');
await page.locator('#workflow_dispatch_dropdown>button').click();
await page.type('input[name="inputs[string2]"]', 'abc');
await page.locator('#workflow-dispatch-submit').click();
await page.waitForLoadState('networkidle');
await expect(page.getByText('Workflow run was successfully requested.')).toBeVisible();
@ -85,7 +79,6 @@ test('workflow dispatch success', async ({browser}, workerInfo) => {
test('workflow dispatch box not available for unauthenticated users', async ({page}) => {
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
await page.waitForLoadState('networkidle');
await expect(page.locator('body')).not.toContainText(workflow_trigger_notification_text);
});

View file

@ -29,15 +29,16 @@ func initChangedFiles() {
globalPatterns := []string{
// meta and config
"Makefile",
"playwright.config.js",
"playwright.config.ts",
".forgejo/workflows/testing.yml",
"tests/e2e/*.go",
"tests/e2e/shared/*",
// frontend files
"frontend/*.js",
"frontend/{base,index}.css",
// templates
"web_src/js/{index,utils}.*",
"web_src/css/{base,index}.css",
// templates and helpers
"templates/base/**",
"modules/templates/**",
}
fullRunPatterns := []glob.Glob{}
for _, expr := range globalPatterns {

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');
@ -17,10 +15,9 @@ test('Correct link and tooltip', async ({browser}, workerInfo) => {
const response = await page.goto('/?repo-search-query=test_workflows');
expect(response?.status()).toBe(200);
await page.waitForLoadState('networkidle');
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('data-tooltip-content', 'Failure');
});

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');
@ -58,7 +56,7 @@ test('Always focus edit tab first on edit', async ({browser}, workerInfo) => {
await page.locator('#issue-1 .comment-container a[data-tab-for=markdown-previewer]').click();
await page.click('#issue-1 .comment-container .save');
await page.waitForLoadState('networkidle');
await page.waitForLoadState();
// Edit again and assert that edit tab should be active (and not preview tab)
await page.click('#issue-1 .comment-container .context-menu');

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/repo/issue/view_content/**
// web_src/css/repo/issue-**
@ -7,98 +5,143 @@
// @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');
});
// belongs to test: Pull: Toggle WIP
const prTitle = 'pull5';
async function click_toggle_wip({page}) {
await page.locator('.toggle-wip>a').click();
await page.waitForLoadState('networkidle');
}
async function check_wip({page}, is) {
const elemTitle = '#issue-title-display';
const stateLabel = '.issue-state-label';
await expect(page.locator(elemTitle)).toContainText(prTitle);
await expect(page.locator(elemTitle)).toContainText('#5');
if (is) {
await expect(page.locator(elemTitle)).toContainText('WIP');
await expect(page.locator(stateLabel)).toContainText('Draft');
} else {
await expect(page.locator(elemTitle)).not.toContainText('WIP');
await expect(page.locator(stateLabel)).toContainText('Open');
/* eslint-disable playwright/expect-expect */
// some tests are reported to have no assertions,
// which is not correct, because they use the global helper function
test.describe('Pull: Toggle WIP', () => {
const prTitle = 'pull5';
async function toggle_wip_to({page}, should) {
await page.waitForLoadState('domcontentloaded');
if (should) {
await page.getByText('Still in progress?').click();
} else {
await page.getByText('Ready for review?').click();
}
}
}
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');
expect(response?.status()).toBe(200); // Status OK
// initial state
await check_wip({page}, false);
// toggle to WIP
await click_toggle_wip({page});
await check_wip({page}, true);
// remove WIP
await click_toggle_wip({page});
await check_wip({page}, false);
async function check_wip({page}, is) {
const elemTitle = 'h1';
const stateLabel = '.issue-state-label';
await page.waitForLoadState('domcontentloaded');
await expect(page.locator(elemTitle)).toContainText(prTitle);
await expect(page.locator(elemTitle)).toContainText('#5');
if (is) {
await expect(page.locator(elemTitle)).toContainText('WIP');
await expect(page.locator(stateLabel)).toContainText('Draft');
} else {
await expect(page.locator(elemTitle)).not.toContainText('WIP');
await expect(page.locator(stateLabel)).toContainText('Open');
}
}
// manually edit title to another prefix
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(`[WIP] ${prTitle}`);
await page.getByText('Save').click();
await page.waitForLoadState('networkidle');
await check_wip({page}, true);
// remove again
await click_toggle_wip({page});
await check_wip({page}, false);
// check maximum title length is handled gracefully
const maxLenStr = prTitle + 'a'.repeat(240);
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(maxLenStr);
await page.getByText('Save').click();
await page.waitForLoadState('networkidle');
await click_toggle_wip({page});
await check_wip({page}, true);
await click_toggle_wip({page});
await check_wip({page}, false);
await expect(page.locator('h1')).toContainText(maxLenStr);
// restore original title
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(prTitle);
await page.getByText('Save').click();
await check_wip({page}, false);
test.beforeEach(async ({browser}, workerInfo) => {
const page = await login({browser}, workerInfo);
const response = await page.goto('/user2/repo1/pulls/5');
expect(response?.status()).toBe(200); // Status OK
// ensure original title
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(prTitle);
await page.getByText('Save').click();
await check_wip({page}, false);
});
test('simple toggle', 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);
await page.goto('/user2/repo1/pulls/5');
// toggle to WIP
await toggle_wip_to({page}, true);
await check_wip({page}, true);
// remove WIP
await toggle_wip_to({page}, false);
await check_wip({page}, false);
});
test('manual edit', 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);
await page.goto('/user2/repo1/pulls/5');
// manually edit title to another prefix
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(`[WIP] ${prTitle}`);
await page.getByText('Save').click();
await check_wip({page}, true);
// remove again
await toggle_wip_to({page}, false);
await check_wip({page}, false);
});
test('maximum title length', 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);
await page.goto('/user2/repo1/pulls/5');
// check maximum title length is handled gracefully
const maxLenStr = prTitle + 'a'.repeat(240);
await page.locator('#issue-title-edit-show').click();
await page.locator('#issue-title-editor input').fill(maxLenStr);
await page.getByText('Save').click();
await expect(page.locator('h1')).toContainText(maxLenStr);
await check_wip({page}, false);
await toggle_wip_to({page}, true);
await check_wip({page}, true);
await expect(page.locator('h1')).toContainText(maxLenStr);
await toggle_wip_to({page}, false);
await check_wip({page}, false);
await expect(page.locator('h1')).toContainText(maxLenStr);
});
});
/* eslint-enable playwright/expect-expect */
test('Issue: Labels', 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');
async function submitLabels({page}) {
const submitted = page.waitForResponse('/user2/repo1/issues/labels');
await page.locator('textarea').first().click(); // close via unrelated element
await submitted;
await page.waitForLoadState();
}
const page = await login({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');
expect(response?.status()).toBe(200);
// preconditions
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
// restore initial state
await page.locator('.select-label').click();
const responsePromise = page.waitForResponse('/user2/repo1/issues/labels');
await page.getByText('Clear labels').click();
await responsePromise;
await expect(labelList.filter({hasText: 'label1'})).toBeHidden();
await expect(labelList.filter({hasText: 'label2'})).toBeHidden();
// add label2
// add both labels
await page.locator('.select-label').click();
// label search could be tested this way:
// await page.locator('.select-label input').fill('label2');
await page.locator('.select-label .item').filter({hasText: 'label2'}).click();
await page.locator('.select-label').click();
await page.waitForLoadState('networkidle');
await page.locator('.select-label .item').filter({hasText: 'label1'}).click();
await submitLabels({page});
await expect(labelList.filter({hasText: 'label2'})).toBeVisible();
// test removing label again
await page.locator('.select-label').click();
await page.locator('.select-label .item').filter({hasText: 'label2'}).click();
await page.locator('.select-label').click();
await page.waitForLoadState('networkidle');
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
// test removing label2 again
// due to a race condition, the page could still be "reloading",
// closing the dropdown after it was clicked.
// Retry the interaction as a group
// also see https://playwright.dev/docs/test-assertions#expecttopass
await expect(async () => {
await page.locator('.select-label').click();
await page.locator('.select-label .item').filter({hasText: 'label2'}).click();
}).toPass();
await submitLabels({page});
await expect(labelList.filter({hasText: 'label2'})).toBeHidden();
await expect(labelList.filter({hasText: 'label1'})).toBeVisible();
});
@ -111,11 +154,6 @@ test('Issue: Assignees', async ({browser}, workerInfo) => {
const response = await page.goto('/org3/repo3/issues/1');
expect(response?.status()).toBe(200);
// preconditions
await expect(assigneesList.filter({hasText: 'user2'})).toBeVisible();
await expect(assigneesList.filter({hasText: 'user4'})).toBeHidden();
await expect(page.locator('.ui.assignees.list .item.no-select')).toBeHidden();
// Clear all assignees
await page.locator('.select-assignees-modify.dropdown').click();
await page.locator('.select-assignees-modify.dropdown .no-select.item').click();

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,16 +1,13 @@
// @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');
expect(response?.status()).toBe(200);
await page.waitForLoadState('networkidle');
const comment = page.locator('.comment-body>.markup', {hasText: 'test markup light/dark-mode-only'});
await expect(comment).toBeVisible();

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');
@ -15,7 +13,6 @@ test('Follow actions', async ({browser}, workerInfo) => {
const page = await context.newPage();
await page.goto('/user1');
await page.waitForLoadState('networkidle');
// Check if following and then unfollowing works.
// This checks that the event listeners of

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,11 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');
});
import {test} from './utils_e2e.ts';
async function assertSelectedLines(page, nums) {
const pageAssertions = async () => {
@ -35,10 +29,7 @@ async function assertSelectedLines(page, nums) {
return pageAssertions();
}
test('Line Range Selection', async ({browser}, workerInfo) => {
const context = await load_logged_in_context(browser, workerInfo, 'user2');
const page = await context.newPage();
test('Line Range Selection', async ({page}) => {
const filePath = '/user2/repo1/src/branch/master/README.md?display=source';
const response = await page.goto(filePath);

View file

@ -1,5 +1,3 @@
// @ts-check
// @watch start
// templates/repo/graph.tmpl
// web_src/css/features/gitgraph.css
@ -7,11 +5,7 @@
// @watch end
import {expect} from '@playwright/test';
import {test, login_user, load_logged_in_context} from './utils_e2e.js';
test.beforeAll(async ({browser}, workerInfo) => {
await login_user(browser, workerInfo, 'user2');
});
import {test} from './utils_e2e.ts';
test('Commit graph overflow', async ({page}) => {
await page.goto('/user2/diff-test/graph');
@ -20,9 +14,7 @@ test('Commit graph overflow', async ({page}) => {
await expect(page.locator('.selection.search.dropdown')).toBeInViewport({ratio: 1});
});
test('Switch branch', async ({browser}, workerInfo) => {
const context = await load_logged_in_context(browser, workerInfo, 'user2');
const page = await context.newPage();
test('Switch branch', async ({page}) => {
const response = await page.goto('/user2/repo1/graph');
expect(response?.status()).toBe(200);
@ -31,7 +23,7 @@ test('Switch branch', async ({browser}, workerInfo) => {
await input.pressSequentially('develop', {delay: 50});
await input.press('Enter');
await page.waitForLoadState('networkidle');
await page.waitForLoadState();
await expect(page.locator('#loading-indicator')).toBeHidden();
await expect(page.locator('#rel-container')).toBeVisible();

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,17 +1,14 @@
// @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');
await page.goto('/user2/repo1/wiki');
await page.waitForLoadState('networkidle');
await page.getByPlaceholder('Search wiki').fill('spaces');
await page.getByPlaceholder('Search wiki').click();
// workaround: HTMX listens on keyup events, playwright's fill only triggers the input event

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);
@ -37,7 +37,7 @@ export async function login_user(browser, workerInfo, user) {
await page.type('input[name=password]', LOGIN_PASSWORD);
await page.click('form button.ui.primary.button:visible');
await page.waitForLoadState('networkidle');
await page.waitForLoadState();
expect(page.url(), {message: `Failed to login user ${user}`}).toBe(`${workerInfo.project.use.baseURL}/`);
@ -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,15 +59,15 @@ 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');
await page.waitForLoadState('domcontentloaded');
// Mock page/version string
await page.locator('footer div.ui.left').evaluate((node) => node.innerHTML = 'MOCK');
await expect(page).toHaveScreenshot({
@ -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, login_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?!
},
});
@ -39,8 +38,10 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
await page.getByText('Add security key').click();
// Logout.
await page.locator('div[aria-label="Profile and settings…"]').click();
await page.getByText('Sign Out').click();
await expect(async () => {
await page.locator('div[aria-label="Profile and settings…"]').click();
await page.getByText('Sign Out').click();
}).toPass();
await page.waitForURL(`${workerInfo.project.use.baseURL}/`);
// Login.
@ -58,5 +59,8 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) =>
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`);
await page.waitForLoadState();
// verify the user can login without a key
await login_user(browser, workerInfo, username);
});

View file

@ -328,7 +328,9 @@ jobs:
sha, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
require.NoError(t, err)
// verify the commit status changes to CommitStatusSuccess when the job changes to StatusSuccess
assert.True(t, checkCommitStatus(sha, context, api.CommitStatusPending))
require.Eventually(t, func() bool {
return checkCommitStatus(sha, context, api.CommitStatusPending)
}, 30*time.Second, 1*time.Second)
for _, actionRun := range actionRuns {
// verify the expected ActionRunJob was created and is StatusWaiting
job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{RunID: actionRun.ID, CommitSHA: sha})
@ -339,7 +341,9 @@ jobs:
actions_service.CreateCommitStatus(db.DefaultContext, job)
}
// verify the commit status changed to CommitStatusSuccess because the job(s) changed to StatusSuccess
assert.True(t, checkCommitStatus(sha, context, api.CommitStatusSuccess))
require.Eventually(t, func() bool {
return checkCommitStatus(sha, context, api.CommitStatusSuccess)
}, 30*time.Second, 1*time.Second)
testCase.assert(t, sha, testCase.onType, testCase.action, actionRuns)
})