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:
commit
1898f3d82a
|
@ -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:
|
||||
|
|
1
Makefile
1
Makefile
|
@ -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
|
||||
|
|
|
@ -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
2
go.mod
|
@ -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
4
go.sum
|
@ -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
269
package-lock.json
generated
|
@ -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"
|
||||
|
|
18
package.json
18
package.json
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
~~~
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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 {
|
||||
|
|
|
@ -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');
|
||||
});
|
|
@ -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)
|
||||
|
|
|
@ -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');
|
|
@ -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');
|
|
@ -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');
|
|
@ -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();
|
|
@ -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');
|
|
@ -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();
|
|
@ -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');
|
|
@ -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
|
|
@ -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');
|
|
@ -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');
|
|
@ -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);
|
|
@ -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();
|
|
@ -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'));
|
||||
|
|
@ -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');
|
|
@ -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
|
|
@ -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');
|
|
@ -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')
|
|
@ -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: {
|
|
@ -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);
|
||||
});
|
|
@ -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)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue