From 78e4370b9892adc998a5c1fd2ee8996962cb8e49 Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Tue, 3 Sep 2024 06:09:40 -0700 Subject: [PATCH] web: Adjust Wdio MaxInstances, add Knip (#11089) - Adjust the WebdriverIO configuration so that `maxInstances` can be set by the environment - `MAX_INSTANCES=1 CI=true npm run test` will run the headless tests from your command line. - `MAX_INSTANCES=1 npm run test-watch` if you want to watch the test run in-browser. - Adds `knip` import tracing facility for linting. - Knip can be accessed by `npm run lint:imports`. Running `MAX_INSTANCES=10` (the default) would sometimes create conflicts and overwhelm the test runner, leaving you with 11 open instances of Chrome and no way to know which one is the one you *don't* want to close. A better choice is `MAX_INSTANCES=1 npm run test-watch`, which would allow the developer to watch the test run serially. Knip adds a new linting feature: tracking down which imports are not used, are not exported correctly, or shouldn't have been exported at all. Despite the "zero-config" promise, it still required significant configuration to handle the wide variety of "strengths" of ESlint configurations, as well as pointers to our entries and reminders that web components may export their classes but their actual use is as part of the component registry. Knip's analyzer produces a lot of false positives. It is not intended to be used as part of the CI/CD pipeline; it is there to help developers figure out what can and should be cleaned up manually. Co-authored-by: Jens Langhammer --- web/package-lock.json | 292 ++++++++++++++++++++++++++++++++++++- web/package.json | 11 +- web/scripts/knip.config.ts | 48 ++++++ web/wdio.conf.ts | 8 +- 4 files changed, 352 insertions(+), 7 deletions(-) create mode 100644 web/scripts/knip.config.ts diff --git a/web/package-lock.json b/web/package-lock.json index a2e2651530..2a81005b40 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -83,6 +83,7 @@ "@types/eslint__js": "^8.42.3", "@types/grecaptcha": "^3.0.9", "@types/guacamole-common-js": "1.5.2", + "@types/node": "^22.5.0", "@types/showdown": "^2.0.6", "@typescript-eslint/eslint-plugin": "^8.0.1", "@typescript-eslint/parser": "^8.0.1", @@ -102,6 +103,7 @@ "github-slugger": "^2.0.0", "glob": "^11.0.0", "globals": "^15.9.0", + "knip": "^5.27.4", "lit-analyzer": "^2.0.3", "lockfile-lint": "^4.14.0", "npm-run-all": "^4.1.5", @@ -6101,6 +6103,50 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@snyk/github-codeowners": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz", + "integrity": "sha512-lGFf08pbkEac0NYgVf4hdANpAgApRjNByLXB+WBip3qj1iendOIyAwP2GKkKbQMNVy2r1xxDf0ssfWscoiC+Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^4.1.1", + "ignore": "^5.1.8", + "p-map": "^4.0.0" + }, + "bin": { + "github-codeowners": "dist/cli.js" + }, + "engines": { + "node": ">=8.10" + } + }, + "node_modules/@snyk/github-codeowners/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@snyk/github-codeowners/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@spotlightjs/overlay": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@spotlightjs/overlay/-/overlay-2.4.0.tgz", @@ -8911,11 +8957,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.1.0", + "version": "22.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz", + "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.13.0" + "undici-types": "~6.19.2" } }, "node_modules/@types/normalize-package-data": { @@ -10383,6 +10431,20 @@ "node": ">= 14" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "dev": true, @@ -11698,6 +11760,16 @@ "consola": "^3.2.3" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "dev": true, @@ -13238,6 +13310,20 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/enquirer": { "version": "2.4.1", "dev": true, @@ -16186,6 +16272,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "dev": true, @@ -17243,6 +17339,16 @@ "node": ">=8" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/jquery": { "version": "3.7.1", "license": "MIT" @@ -17519,6 +17625,131 @@ "node": ">=6" } }, + "node_modules/knip": { + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.29.1.tgz", + "integrity": "sha512-l8qFtRqNpCk8xf46VOwhBUva7LBwanoGPJ4KQNwVRl6hmEXStf1BJlfbYRZ+yQpbilbIV6LN+ztX6LaGtyd4TQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + }, + { + "type": "polar", + "url": "https://polar.sh/webpro-nl" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "1.2.8", + "@snyk/github-codeowners": "1.1.0", + "easy-table": "1.2.0", + "enhanced-resolve": "^5.17.1", + "fast-glob": "^3.3.2", + "jiti": "^1.21.6", + "js-yaml": "^4.1.0", + "minimist": "^1.2.8", + "picocolors": "^1.0.0", + "picomatch": "^4.0.1", + "pretty-ms": "^9.0.0", + "smol-toml": "^1.1.4", + "strip-json-comments": "5.0.1", + "summary": "2.1.0", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.6.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4" + } + }, + "node_modules/knip/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/knip/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/knip/node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/knip/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/knip/node_modules/pretty-ms": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.1.0.tgz", + "integrity": "sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", + "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -21942,6 +22173,19 @@ "dev": true, "license": "MIT" }, + "node_modules/smol-toml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.0.tgz", + "integrity": "sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/socks": { "version": "2.8.3", "dev": true, @@ -22904,6 +23148,13 @@ "version": "4.3.2", "license": "MIT" }, + "node_modules/summary": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/summary/-/summary-2.1.0.tgz", + "integrity": "sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==", + "dev": true, + "license": "MIT" + }, "node_modules/supports-color": { "version": "5.5.0", "dev": true, @@ -23312,6 +23563,16 @@ "node": ">=8" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tar": { "version": "6.2.1", "dev": true, @@ -23997,7 +24258,9 @@ } }, "node_modules/undici-types": { - "version": "6.13.0", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, "license": "MIT" }, @@ -25294,6 +25557,29 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.3.1.tgz", + "integrity": "sha512-uFzCZz7FQis256dqw4AhPQgD6f3pzNca/Zh62RNELavlumQB3nDIUFbF5JQfFLcMbO1s02Q7Xg/gpcOBlEnYZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } + }, "packages/sfe": { "name": "@goauthentik/web-sfe", "version": "0.0.0", diff --git a/web/package.json b/web/package.json index 08c866cef3..f7b45577c7 100644 --- a/web/package.json +++ b/web/package.json @@ -71,6 +71,7 @@ "@types/eslint__js": "^8.42.3", "@types/grecaptcha": "^3.0.9", "@types/guacamole-common-js": "1.5.2", + "@types/node": "^22.5.0", "@types/showdown": "^2.0.6", "@typescript-eslint/eslint-plugin": "^8.0.1", "@typescript-eslint/parser": "^8.0.1", @@ -90,6 +91,7 @@ "github-slugger": "^2.0.0", "glob": "^11.0.0", "globals": "^15.9.0", + "knip": "^5.27.4", "lit-analyzer": "^2.0.3", "lockfile-lint": "^4.14.0", "npm-run-all": "^4.1.5", @@ -135,6 +137,7 @@ "extract-locales": "wireit", "format": "wireit", "lint": "wireit", + "lint:imports": "wireit", "lint:lockfile": "wireit", "lint:nightmare": "wireit", "lint:package": "wireit", @@ -149,8 +152,7 @@ "storybook:build": "wireit", "storybook:build-import-map": "wireit", "test": "wireit", - "test-view": "wireit", - "test-watch": "npx wdio run ./wdio.conf.ts --autoCompileOpts.tsNodeOpts.project=tsconfig.test.json --watch", + "test-watch": "wireit", "tsc": "wireit", "watch": "run-s build-locales esbuild:watch" }, @@ -250,6 +252,9 @@ "lint:components": { "command": "lit-analyzer src" }, + "lint:imports": { + "command": "knip --config scripts/knip.config.ts" + }, "lint:types": { "command": "tsc --noEmit -p .", "dependencies": [ @@ -330,7 +335,7 @@ "TS_NODE_PROJECT": "tsconfig.test.json" } }, - "test-view": { + "test-watch": { "command": "wdio run ./wdio.conf.ts", "env": { "TS_NODE_PROJECT": "tsconfig.test.json" diff --git a/web/scripts/knip.config.ts b/web/scripts/knip.config.ts new file mode 100644 index 0000000000..a923bd63ae --- /dev/null +++ b/web/scripts/knip.config.ts @@ -0,0 +1,48 @@ +import { type KnipConfig } from "knip"; + +const config: KnipConfig = { + "entry": [ + "./src/admin/AdminInterface/AdminInterface.ts", + "./src/user/UserInterface.ts", + "./src/flow/FlowInterface.ts", + "./src/standalone/api-browser/index.ts", + "./src/enterprise/rac/index.ts", + "./src/standalone/loading/index.ts", + "./src/polyfill/poly.ts", + ], + "project": ["src/**/*.ts", "src/**/*.js", "./scripts/*.mjs", ".storybook/*.ts"], + // "ignore": ["src/**/*.test.ts", "src/**/*.stories.ts"], + // Prevent Knip from complaining about web components, which export their classes but also + // export their registration, and we don't always use both. + "ignoreExportsUsedInFile": true, + "typescript": { + config: ["tsconfig.json"], + }, + "wireit": { + config: ["package.json"], + }, + "storybook": { + config: [".storybook/{main,test-runner}.{js,ts}"], + entry: [ + ".storybook/{manager,preview}.{js,jsx,ts,tsx}", + "**/*.@(mdx|stories.@(mdx|js|jsx|mjs|ts|tsx))", + ], + project: [".storybook/**/*.{js,jsx,ts,tsx}"], + }, + "eslint": { + entry: [ + "eslint.config.mjs", + "scripts/eslint.precommit.mjs", + "scripts/eslint.nightmare.mjs", + "scripts/eslint-precommit.mjs", + "scripts/eslint-nightmare.mjs", + "scripts/eslint.mjs", + ], + config: ["package.json"], + }, + "webdriver-io": { + config: ["wdio.conf.js"], + }, +}; + +export default config; diff --git a/web/wdio.conf.ts b/web/wdio.conf.ts index 8a78b92c83..3253ca3f75 100644 --- a/web/wdio.conf.ts +++ b/web/wdio.conf.ts @@ -8,6 +8,12 @@ import tsconfigPaths from "vite-tsconfig-paths"; const isProdBuild = process.env.NODE_ENV === "production"; const apiBasePath = process.env.AK_API_BASE_PATH || ""; const runHeadless = process.env.CI !== undefined; +const maxInstances = + process.env.MAX_INSTANCES !== undefined + ? parseInt(process.env.MAX_INSTANCES, 10) + : runHeadless + ? 10 + : 1; export const config: Options.Testrunner = { // @@ -81,7 +87,7 @@ export const config: Options.Testrunner = { // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // - maxInstances: 10, + maxInstances, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: