@warlock.js/core 5.34.3 → 5.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/esm/cli/cli-commands.manager.mjs +1 -1
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -0
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +30 -3
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/check/check-command.action.mjs +1 -0
- package/esm/cli/commands/check/check-command.action.mjs.map +1 -1
- package/esm/cli/commands/check.command.mjs +5 -0
- package/esm/cli/commands/check.command.mjs.map +1 -1
- package/esm/cli/commands/explain/explain-command.action.mjs +21 -0
- package/esm/cli/commands/explain/explain-command.action.mjs.map +1 -1
- package/esm/cli/commands/explain.command.mjs +14 -4
- package/esm/cli/commands/explain.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +19 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/endpoint.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/web.generator.mjs +169 -0
- package/esm/cli/commands/generate/generators/web.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/templates/web-stubs.mjs +231 -0
- package/esm/cli/commands/generate/templates/web-stubs.mjs.map +1 -0
- package/esm/cli/commands/inspect/inspect-command.action.mjs +4 -3
- package/esm/cli/commands/inspect/inspect-command.action.mjs.map +1 -1
- package/esm/cli/commands/inspect.command.mjs +13 -8
- package/esm/cli/commands/inspect.command.mjs.map +1 -1
- package/esm/cli/commands/mcp/mcp-command.action.mjs +1 -1
- package/esm/cli/commands/mcp/mcp-command.action.mjs.map +1 -1
- package/esm/config/config-loader.d.mts.map +1 -1
- package/esm/config/config-loader.mjs +2 -0
- package/esm/config/config-loader.mjs.map +1 -1
- package/esm/config/load-config-files.mjs +4 -0
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/dev-server/file-event-handler.mjs +11 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.d.mts +1 -1
- package/esm/dev-server/file-manager.d.mts.map +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +16 -1
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +13 -4
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/loader/loader-port-acks.mjs +21 -9
- package/esm/dev-server/loader/loader-port-acks.mjs.map +1 -1
- package/esm/dev-server/loader/register-loader.mjs +31 -12
- package/esm/dev-server/loader/register-loader.mjs.map +1 -1
- package/esm/dev-server/module-loader.mjs +15 -1
- package/esm/dev-server/module-loader.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +1 -1
- package/esm/dev-server/special-file-patterns.mjs +4 -1
- package/esm/dev-server/special-file-patterns.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/e2e/cdp.d.mts.map +1 -1
- package/esm/e2e/cdp.mjs +9 -2
- package/esm/e2e/cdp.mjs.map +1 -1
- package/esm/e2e/index.d.mts +2 -2
- package/esm/e2e/page.d.mts +14 -1
- package/esm/e2e/page.d.mts.map +1 -1
- package/esm/e2e/page.mjs +81 -8
- package/esm/e2e/page.mjs.map +1 -1
- package/esm/e2e/types.d.mts +49 -0
- package/esm/e2e/types.d.mts.map +1 -1
- package/esm/e2e/types.mjs.map +1 -1
- package/esm/errors/error-codes.d.mts +72 -0
- package/esm/errors/error-codes.d.mts.map +1 -1
- package/esm/errors/error-codes.mjs +72 -0
- package/esm/errors/error-codes.mjs.map +1 -1
- package/esm/errors/index.d.mts +2 -2
- package/esm/errors/index.mjs +2 -2
- package/esm/generations/add-command.action.mjs +2 -0
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/generations/design-system-payload.mjs +53 -0
- package/esm/generations/design-system-payload.mjs.map +1 -0
- package/esm/generations/features/design-system.feature.mjs +38 -0
- package/esm/generations/features/design-system.feature.mjs.map +1 -0
- package/esm/generations/features/index.mjs +3 -1
- package/esm/generations/features/index.mjs.map +1 -1
- package/esm/generations/features/notifications.feature.mjs +13 -2
- package/esm/generations/features/notifications.feature.mjs.map +1 -1
- package/esm/generations/features/test.feature.mjs +48 -13
- package/esm/generations/features/test.feature.mjs.map +1 -1
- package/esm/generations/features/web.feature.mjs +3 -3
- package/esm/generations/features/web.feature.mjs.map +1 -1
- package/esm/generations/stubs.mjs +9 -5
- package/esm/generations/stubs.mjs.map +1 -1
- package/esm/http/error-mapper.d.mts +1 -1
- package/esm/http/error-mapper.mjs +3 -3
- package/esm/http/error-mapper.mjs.map +1 -1
- package/esm/http/error-response.d.mts +3 -0
- package/esm/http/error-response.d.mts.map +1 -1
- package/esm/http/error-response.mjs +8 -2
- package/esm/http/error-response.mjs.map +1 -1
- package/esm/http/errors/errors.d.mts +8 -0
- package/esm/http/errors/errors.d.mts.map +1 -1
- package/esm/http/errors/errors.mjs +13 -1
- package/esm/http/errors/errors.mjs.map +1 -1
- package/esm/http/index.d.mts +3 -2
- package/esm/http/index.mjs +3 -2
- package/esm/http/list-endpoint.d.mts +35 -0
- package/esm/http/list-endpoint.d.mts.map +1 -0
- package/esm/http/list-endpoint.mjs +28 -0
- package/esm/http/list-endpoint.mjs.map +1 -0
- package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
- package/esm/http/middleware/inject-request-context.mjs +12 -2
- package/esm/http/middleware/inject-request-context.mjs.map +1 -1
- package/esm/http/server.mjs +1 -1
- package/esm/index.d.mts +10 -6
- package/esm/index.mjs +5 -2
- package/esm/localization/index.mjs +3 -1
- package/esm/localization/translation-usage-graph.d.mts +49 -0
- package/esm/localization/translation-usage-graph.d.mts.map +1 -0
- package/esm/localization/translation-usage-graph.mjs +128 -0
- package/esm/localization/translation-usage-graph.mjs.map +1 -0
- package/esm/localization/translation-usage.d.mts +85 -0
- package/esm/localization/translation-usage.d.mts.map +1 -0
- package/esm/localization/translation-usage.mjs +307 -0
- package/esm/localization/translation-usage.mjs.map +1 -0
- package/esm/openapi/security.mjs +12 -4
- package/esm/openapi/security.mjs.map +1 -1
- package/esm/openapi/types.d.mts +2 -0
- package/esm/openapi/types.d.mts.map +1 -1
- package/esm/production/build-budgets.d.mts +64 -0
- package/esm/production/build-budgets.d.mts.map +1 -0
- package/esm/production/build-budgets.mjs +109 -0
- package/esm/production/build-budgets.mjs.map +1 -0
- package/esm/production/production-builder.mjs +13 -1
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.d.mts +1 -1
- package/esm/production/resolve-build-config.d.mts.map +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/production/route-registration-snapshot.mjs +5 -3
- package/esm/production/route-registration-snapshot.mjs.map +1 -1
- package/esm/restful/restful.d.mts +1 -1
- package/esm/router/router.d.mts +2 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +4 -1
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +19 -0
- package/esm/router/types.d.mts.map +1 -1
- package/esm/tests/fakes/storage-fake.d.mts +3 -1
- package/esm/tests/fakes/storage-fake.d.mts.map +1 -1
- package/esm/tests/fakes/storage-fake.mjs +3 -3
- package/esm/tests/fakes/storage-fake.mjs.map +1 -1
- package/esm/tests/fakes/test-fakes.d.mts +3 -1
- package/esm/tests/fakes/test-fakes.d.mts.map +1 -1
- package/esm/tests/fakes/test-fakes.mjs +3 -2
- package/esm/tests/fakes/test-fakes.mjs.map +1 -1
- package/esm/tests/index.d.mts +4 -2
- package/esm/tests/index.mjs +3 -1
- package/esm/tests/load-unit-config.mjs +24 -0
- package/esm/tests/load-unit-config.mjs.map +1 -0
- package/esm/tests/response-contract.d.mts +32 -0
- package/esm/tests/response-contract.d.mts.map +1 -0
- package/esm/tests/response-contract.mjs +116 -0
- package/esm/tests/response-contract.mjs.map +1 -0
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +10 -2
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-lifecycle-state.mjs.map +1 -1
- package/esm/tests/test-locales.mjs +33 -0
- package/esm/tests/test-locales.mjs.map +1 -0
- package/esm/tests/test-runtime-scope.d.mts +15 -0
- package/esm/tests/test-runtime-scope.d.mts.map +1 -0
- package/esm/tests/test-runtime-scope.mjs +80 -0
- package/esm/tests/test-runtime-scope.mjs.map +1 -0
- package/esm/tests/vitest-setup.d.mts +13 -3
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +146 -18
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/tooling/check/affected-files.d.mts +5 -0
- package/esm/tooling/check/affected-files.d.mts.map +1 -0
- package/esm/tooling/check/affected-files.mjs +56 -0
- package/esm/tooling/check/affected-files.mjs.map +1 -0
- package/esm/tooling/check/api-route-references.d.mts +16 -0
- package/esm/tooling/check/api-route-references.d.mts.map +1 -0
- package/esm/tooling/check/api-route-references.mjs +146 -0
- package/esm/tooling/check/api-route-references.mjs.map +1 -0
- package/esm/tooling/check/changed-files.d.mts +2 -0
- package/esm/tooling/check/changed-files.d.mts.map +1 -1
- package/esm/tooling/check/changed-files.mjs +2 -2
- package/esm/tooling/check/changed-files.mjs.map +1 -1
- package/esm/tooling/check/conventions-step.d.mts +7 -3
- package/esm/tooling/check/conventions-step.d.mts.map +1 -1
- package/esm/tooling/check/conventions-step.mjs +59 -8
- package/esm/tooling/check/conventions-step.mjs.map +1 -1
- package/esm/tooling/check/fixes.d.mts +8 -0
- package/esm/tooling/check/fixes.d.mts.map +1 -0
- package/esm/tooling/check/fixes.mjs +31 -0
- package/esm/tooling/check/fixes.mjs.map +1 -0
- package/esm/tooling/check/index.d.mts +12 -0
- package/esm/tooling/check/index.mjs +3 -2
- package/esm/tooling/check/run-check.d.mts +2 -0
- package/esm/tooling/check/run-check.d.mts.map +1 -1
- package/esm/tooling/check/run-check.mjs +23 -5
- package/esm/tooling/check/run-check.mjs.map +1 -1
- package/esm/tooling/check/run-tests.mjs +1 -1
- package/esm/tooling/check/source-ast.d.mts +1 -0
- package/esm/tooling/check/translation-conventions.mjs +32 -0
- package/esm/tooling/check/translation-conventions.mjs.map +1 -0
- package/esm/tooling/check/web-conventions.d.mts +10 -0
- package/esm/tooling/check/web-conventions.d.mts.map +1 -0
- package/esm/tooling/check/web-conventions.mjs +126 -0
- package/esm/tooling/check/web-conventions.mjs.map +1 -0
- package/esm/tooling/index.d.mts +7 -2
- package/esm/tooling/index.mjs +4 -2
- package/esm/tooling/inspect/collect-files.mjs +172 -0
- package/esm/tooling/inspect/collect-files.mjs.map +1 -0
- package/esm/tooling/inspect/collect-pages.mjs +3 -2
- package/esm/tooling/inspect/collect-pages.mjs.map +1 -1
- package/esm/tooling/inspect/collect-routes.mjs +1 -1
- package/esm/tooling/inspect/collect-routes.mjs.map +1 -1
- package/esm/tooling/inspect/explain-file.d.mts +12 -0
- package/esm/tooling/inspect/explain-file.d.mts.map +1 -0
- package/esm/tooling/inspect/explain-file.mjs +45 -0
- package/esm/tooling/inspect/explain-file.mjs.map +1 -0
- package/esm/tooling/inspect/index.d.mts +3 -2
- package/esm/tooling/inspect/index.mjs +2 -1
- package/esm/tooling/inspect/inspect-app.d.mts.map +1 -1
- package/esm/tooling/inspect/inspect-app.mjs +9 -3
- package/esm/tooling/inspect/inspect-app.mjs.map +1 -1
- package/esm/tooling/inspect/inspect-report.schema.mjs +34 -1
- package/esm/tooling/inspect/inspect-report.schema.mjs.map +1 -1
- package/esm/tooling/inspect/inspect-report.types.d.mts +17 -1
- package/esm/tooling/inspect/inspect-report.types.d.mts.map +1 -1
- package/esm/tooling/inspect/inspect-report.types.mjs +2 -1
- package/esm/tooling/inspect/inspect-report.types.mjs.map +1 -1
- package/esm/tooling/mcp/mcp-tools.d.mts.map +1 -1
- package/esm/tooling/mcp/mcp-tools.mjs +8 -2
- package/esm/tooling/mcp/mcp-tools.mjs.map +1 -1
- package/esm/tooling/problem.d.mts +4 -0
- package/esm/tooling/problem.d.mts.map +1 -1
- package/esm/tooling/problem.mjs +1 -0
- package/esm/tooling/problem.mjs.map +1 -1
- package/esm/tooling/syntax/ast.d.mts +2 -0
- package/esm/tooling/typecheck/run-typecheck.mjs +5 -1
- package/esm/tooling/typecheck/run-typecheck.mjs.map +1 -1
- package/esm/upgrade/codemods/index.mjs +2 -0
- package/esm/upgrade/codemods/index.mjs.map +1 -1
- package/esm/upgrade/codemods/vite-vitest.mjs +170 -0
- package/esm/upgrade/codemods/vite-vitest.mjs.map +1 -0
- package/esm/upgrade/notes.mjs +4 -0
- package/esm/upgrade/notes.mjs.map +1 -1
- package/esm/vite/index.d.mts +3 -1
- package/esm/vite/index.mjs +3 -1
- package/esm/vite/test-setup-reporter.d.mts +16 -0
- package/esm/vite/test-setup-reporter.d.mts.map +1 -0
- package/esm/vite/test-setup-reporter.mjs +55 -0
- package/esm/vite/test-setup-reporter.mjs.map +1 -0
- package/esm/vite/test-workers.d.mts +5 -0
- package/esm/vite/test-workers.d.mts.map +1 -0
- package/esm/vite/test-workers.mjs +38 -0
- package/esm/vite/test-workers.mjs.map +1 -0
- package/esm/warlock-config/index.d.mts +2 -1
- package/esm/warlock-config/types.d.mts +3 -0
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/llms-full.txt +874 -9
- package/llms.txt +2 -0
- package/package.json +15 -14
- package/schemas/inspect-report.schema.json +47 -1
- package/skills/build-budgets/SKILL.md +51 -0
- package/skills/build-restful/SKILL.md +56 -0
- package/skills/configure-app/SKILL.md +7 -0
- package/skills/create-module/SKILL.md +9 -0
- package/skills/generate-web-ui/SKILL.md +71 -0
- package/skills/index.md +2 -2
- package/skills/register-route/SKILL.md +13 -0
- package/skills/run-app/SKILL.md +1 -1
- package/skills/send-mail/SKILL.md +25 -0
- package/skills/send-response/SKILL.md +50 -0
- package/skills/test-browser/SKILL.md +53 -0
- package/skills/test-http/SKILL.md +91 -0
- package/skills/test-service/SKILL.md +161 -3
- package/skills/upgrade-from-5-29/SKILL.md +109 -0
- package/skills/use-localization/SKILL.md +25 -0
- package/skills/warlock-check/SKILL.md +75 -1
- package/skills/warlock-inspect/SKILL.md +45 -2
- package/skills/warlock-mcp/SKILL.md +4 -0
- package/skills/write-cli-command/SKILL.md +18 -0
- package/esm/production/build-app-production.mjs +0 -14
- package/esm/production/build-app-production.mjs.map +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
//#region ../../packages/core/src/dev-server/special-file-patterns.ts
|
|
2
2
|
const normalize = (path) => path.replace(/\\/g, "/");
|
|
3
3
|
/** `src/config/**` — any depth, the config key is the raw path */
|
|
4
|
-
const isConfigFile = (path) =>
|
|
4
|
+
const isConfigFile = (path) => {
|
|
5
|
+
const normalized = normalize(path);
|
|
6
|
+
return /^src\/config\/.*\.(ts|tsx)$/.test(normalized) && !/\.(spec|test)\.[^/]+$/.test(normalized) && !/\/(?:__tests__|__mocks__|__fixtures__|tests?|fixtures)\//.test(normalized);
|
|
7
|
+
};
|
|
5
8
|
/** `src/app/main.ts` or `src/app/<module>/main.ts` */
|
|
6
9
|
const isMainFile = (path) => /^src\/app\/([^/]+\/)?main\.(ts|tsx)$/.test(normalize(path));
|
|
7
10
|
/** `src/app/<module>/routes.ts` — one level deep */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"special-file-patterns.mjs","names":[],"sources":["../../../../../../../../packages/core/src/dev-server/special-file-patterns.ts"],"sourcesContent":["const normalize = (path: string) => path.replace(/\\\\/g, \"/\");\n\n/** `src/config/**` — any depth, the config key is the raw path */\nexport const isConfigFile = (path: string) => /^src\\/config\\/.*\\.(ts|tsx)$/.test(
|
|
1
|
+
{"version":3,"file":"special-file-patterns.mjs","names":[],"sources":["../../../../../../../../packages/core/src/dev-server/special-file-patterns.ts"],"sourcesContent":["const normalize = (path: string) => path.replace(/\\\\/g, \"/\");\n\n/** `src/config/**` — any depth, the config key is the raw path */\nexport const isConfigFile = (path: string) => {\n const normalized = normalize(path);\n return (\n /^src\\/config\\/.*\\.(ts|tsx)$/.test(normalized) &&\n !/\\.(spec|test)\\.[^/]+$/.test(normalized) &&\n !/\\/(?:__tests__|__mocks__|__fixtures__|tests?|fixtures)\\//.test(normalized)\n );\n};\n\n/** `src/app/main.ts` or `src/app/<module>/main.ts` */\nexport const isMainFile = (path: string) =>\n /^src\\/app\\/([^/]+\\/)?main\\.(ts|tsx)$/.test(normalize(path));\n\n/** `src/app/<module>/routes.ts` — one level deep */\nexport const isRouteFile = (path: string) =>\n /^src\\/app\\/[^/]+\\/routes\\.(ts|tsx)$/.test(normalize(path));\n\n/** `src/app/worker.ts` or `src/app/<module>/worker.ts` — mirrors `isMainFile`'s optional module dir */\nexport const isWorkerFile = (path: string) =>\n /^src\\/app\\/([^/]+\\/)?worker\\.(ts|tsx)$/.test(normalize(path));\n\n/** `src/app/<module>/events/<file>.ts` */\nexport const isEventFile = (path: string) =>\n /^src\\/app\\/[^/]+\\/events\\/[^/]+\\.(ts|tsx)$/.test(normalize(path));\n\n/** `src/app/<module>/utils/locales.ts` or `locales.json` */\nexport const isLocaleFile = (path: string) =>\n /^src\\/app\\/[^/]+\\/utils\\/locales\\.(ts|tsx|json)$/.test(normalize(path));\n\n/** Source files the dev server can hot-reload; anything else is data (`.sql`, `.json`, …) */\nexport const isCodeFile = (path: string) =>\n /\\.(ts|tsx|js|jsx|mjs|cjs|mts|cts)$/.test(normalize(path));\n"],"mappings":";AAAA,MAAM,aAAa,SAAiB,KAAK,QAAQ,OAAO,GAAG;;AAG3D,MAAa,gBAAgB,SAAiB;CAC5C,MAAM,aAAa,UAAU,IAAI;CACjC,OACE,8BAA8B,KAAK,UAAU,KAC7C,CAAC,wBAAwB,KAAK,UAAU,KACxC,CAAC,2DAA2D,KAAK,UAAU;AAE/E;;AAGA,MAAa,cAAc,SACzB,uCAAuC,KAAK,UAAU,IAAI,CAAC;;AAG7D,MAAa,eAAe,SAC1B,sCAAsC,KAAK,UAAU,IAAI,CAAC;;AAG5D,MAAa,gBAAgB,SAC3B,yCAAyC,KAAK,UAAU,IAAI,CAAC;;AAG/D,MAAa,eAAe,SAC1B,6CAA6C,KAAK,UAAU,IAAI,CAAC;;AAGnE,MAAa,gBAAgB,SAC3B,mDAAmD,KAAK,UAAU,IAAI,CAAC;;AAGzE,MAAa,cAAc,SACzB,qCAAqC,KAAK,UAAU,IAAI,CAAC"}
|
|
@@ -2,11 +2,11 @@ import { Path } from "../utils/normalized-path.mjs";
|
|
|
2
2
|
import { warlockPath } from "../utils/paths.mjs";
|
|
3
3
|
import "../utils/index.mjs";
|
|
4
4
|
import { parseLocaleDictionary } from "../localization/parse-locale-dictionary.mjs";
|
|
5
|
+
import { forEachChild, hasModifier, isImportDeclaration, isInterfaceDeclaration, isObjectLiteralExpression, isPropertyAssignment, isShorthandPropertyAssignment, isTypeAliasDeclaration, isVariableDeclaration, keyName, textOf } from "../tooling/syntax/ast.mjs";
|
|
5
6
|
import "../localization/index.mjs";
|
|
6
7
|
import { devLogDim, devLogError, devLogInfo, devLogSuccess, devServeLog } from "./dev-logger.mjs";
|
|
7
8
|
import { parseImports } from "./parse-imports.mjs";
|
|
8
9
|
import { getFilesFromDirectory } from "./utils.mjs";
|
|
9
|
-
import { forEachChild, hasModifier, isImportDeclaration, isInterfaceDeclaration, isObjectLiteralExpression, isPropertyAssignment, isShorthandPropertyAssignment, isTypeAliasDeclaration, isVariableDeclaration, keyName, textOf } from "../tooling/syntax/ast.mjs";
|
|
10
10
|
import { extractTranslationKeys } from "./extract-translation-keys.mjs";
|
|
11
11
|
import { filesOrchestrator } from "./files-orchestrator.mjs";
|
|
12
12
|
import { readConfigAst } from "./read-config-ast.mjs";
|
package/esm/e2e/cdp.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdp.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/cdp.ts"],"mappings":";;;;;;KAKK;EAAiB;EAAiB;;KAClC;EAAqB;EAAc,YAAY;;YACxC;EAEV;IAAoB,QAAQ;IAAuB,QAAQ;;EAE3D;IACE;MAAU;MAAoB;MAAwB;MAAuB;;IAC7E;MAAU,QAAQ;MAAc,mBAAmB;;;EAGrD;IAAiB,QAAQ;IAAuB,QAAQ;;EAExD;IACE;MAAU;;IACV;MAAU;MAAiB;MAAmB;;;EAGhD;IAA4B;MAAU;;IAAiB;MAAU;;;EAEjE;IACE;MAAU;MAAe;MAAgB;MAA2B;;IACpE,QAAQ;;EAGV;IACE;MACE;MACA;MACA;MACA;MACA;MACA;;IAEF,QAAQ;;EAGV;IAAsB;MAAU;;IAAgB,QAAQ;;EAExD;IACE;MAAU;MAA2B;MAAa;MAAc;;IAChE,QAAQ;;EAGV;IAAoB,QAAQ;IAAuB,QAAQ;;EAE3D;IAAgB,QAAQ;IAAuB,QAAQ;;;YAE7C;EAEV;IAAyB;;EAEzB;IAA8B;IAAc,MAAM;;EAElD;IAA6B,kBAAkB;;EAE/C;IAAoB;MAAS;MAAe;;;EAE5C;IAA+B;IAAmB;MAAW;;;EAE7D;IAA8B;IAAmB;MAAY;MAAa;;;EAE1E;IAA2B;IAAmB;IAAmB;;EAEjE;IAA6B;;;
|
|
1
|
+
{"version":3,"file":"cdp.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/cdp.ts"],"mappings":";;;;;;KAKK;EAAiB;EAAiB;;KAClC;EAAqB;EAAc,YAAY;;YACxC;EAEV;IAAoB,QAAQ;IAAuB,QAAQ;;EAE3D;IACE;MAAU;MAAoB;MAAwB;MAAuB;;IAC7E;MAAU,QAAQ;MAAc,mBAAmB;;;EAGrD;IAAiB,QAAQ;IAAuB,QAAQ;;EAExD;IACE;MAAU;;IACV;MAAU;MAAiB;MAAmB;;;EAGhD;IAA4B;MAAU;;IAAiB;MAAU;;;EAEjE;IACE;MAAU;MAAe;MAAgB;MAA2B;;IACpE,QAAQ;;EAGV;IACE;MACE;MACA;MACA;MACA;MACA;MACA;;IAEF,QAAQ;;EAGV;IAAsB;MAAU;;IAAgB,QAAQ;;EAExD;IACE;MAAU;MAA2B;MAAa;MAAc;;IAChE,QAAQ;;EAGV;IAAoB,QAAQ;IAAuB,QAAQ;;EAE3D;IAAgB,QAAQ;IAAuB,QAAQ;;;YAE7C;EAEV;IAAyB;;EAEzB;IAA8B;IAAc,MAAM;;EAElD;IAA6B,kBAAkB;;EAE/C;IAAoB;MAAS;MAAe;;;EAE5C;IAA+B;IAAmB;MAAW;;;EAE7D;IAA8B;IAAmB;MAAY;MAAa;;;EAE1E;IAA2B;IAAmB;IAAmB;;EAEjE;IAA6B;;;qBAgBlB;UAMiB;UALpB;UACA;UACA;UACA;UAED;SA4BM,QAAQ,aAAa,qBAAqB,QAAQ;EAiC/D,QAAQ,gBAAgB,YACtB,QAAQ,GACR,QAAQ,WAAW,cACnB,qBACC,QAAQ,WAAW;EA0BtB,GAAG,gBAAgB,WAAW,QAAQ,GAAG,WAAW,QAAQ,UAAU;UAU9D;EASR"}
|
package/esm/e2e/cdp.mjs
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region ../../packages/core/src/e2e/cdp.ts
|
|
2
|
+
/** A command got no reply within its budget. Retry loops that shrink their budget use this type to tell a timeout apart from a reply that failed. */
|
|
3
|
+
var CdpTimeoutError = class extends Error {
|
|
4
|
+
constructor(timeoutMs, method) {
|
|
5
|
+
super(`CDP timeout after ${timeoutMs}ms: ${method}`);
|
|
6
|
+
this.name = "CdpTimeoutError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
2
9
|
var CdpConnection = class CdpConnection {
|
|
3
10
|
constructor(socket) {
|
|
4
11
|
this.socket = socket;
|
|
@@ -57,7 +64,7 @@ var CdpConnection = class CdpConnection {
|
|
|
57
64
|
const id = ++this.sequence;
|
|
58
65
|
const timer = setTimeout(() => {
|
|
59
66
|
this.pending.delete(id);
|
|
60
|
-
reject(
|
|
67
|
+
reject(new CdpTimeoutError(timeoutMs, method));
|
|
61
68
|
}, Math.max(1, timeoutMs));
|
|
62
69
|
this.pending.set(id, {
|
|
63
70
|
resolve: (value) => resolve(value),
|
|
@@ -102,5 +109,5 @@ var CdpConnection = class CdpConnection {
|
|
|
102
109
|
};
|
|
103
110
|
|
|
104
111
|
//#endregion
|
|
105
|
-
export { CdpConnection };
|
|
112
|
+
export { CdpConnection, CdpTimeoutError };
|
|
106
113
|
//# sourceMappingURL=cdp.mjs.map
|
package/esm/e2e/cdp.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cdp.mjs","names":[],"sources":["../../../../../../../../packages/core/src/e2e/cdp.ts"],"sourcesContent":["/** Wire shapes verified against the official protocol viewer and its source JSON:\n * https://chromedevtools.github.io/devtools-protocol/\n * https://github.com/ChromeDevTools/devtools-protocol/tree/master/json\n * Only the fields consumed by this harness are modeled here.\n */\ntype RemoteObject = { value?: unknown; description?: string };\ntype ExceptionDetails = { text: string; exception?: RemoteObject };\nexport type CdpMethods = {\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable\n \"Runtime.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate\n \"Runtime.evaluate\": {\n params: { expression: string; returnByValue: boolean; awaitPromise: boolean; silent: boolean };\n result: { result: RemoteObject; exceptionDetails?: ExceptionDetails };\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-enable\n \"Page.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-navigate\n \"Page.navigate\": {\n params: { url: string };\n result: { frameId: string; loaderId?: string; errorText?: string };\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot\n \"Page.captureScreenshot\": { params: { format: \"png\" }; result: { data: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride\n \"Emulation.setDeviceMetricsOverride\": {\n params: { width: number; height: number; deviceScaleFactor: number; mobile: boolean };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent\n \"Input.dispatchMouseEvent\": {\n params: {\n type: \"mouseMoved\" | \"mousePressed\" | \"mouseReleased\";\n x: number;\n y: number;\n button?: \"left\";\n buttons?: number;\n clickCount?: number;\n };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-insertText\n \"Input.insertText\": { params: { text: string }; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent\n \"Input.dispatchKeyEvent\": {\n params: { type: \"keyDown\" | \"keyUp\"; key: string; code: string; windowsVirtualKeyCode: number };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-enable\n \"Network.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Log/#method-enable\n \"Log.enable\": { params: Record<string, never>; result: Record<string, never> };\n};\nexport type CdpEvents = {\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-loadEventFired\n \"Page.loadEventFired\": { timestamp: number };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-consoleAPICalled\n \"Runtime.consoleAPICalled\": { type: string; args: RemoteObject[] };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionThrown\n \"Runtime.exceptionThrown\": { exceptionDetails: ExceptionDetails };\n // https://chromedevtools.github.io/devtools-protocol/tot/Log/#event-entryAdded\n \"Log.entryAdded\": { entry: { level: string; text: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-requestWillBeSent\n \"Network.requestWillBeSent\": { requestId: string; request: { url: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-responseReceived\n \"Network.responseReceived\": { requestId: string; response: { url: string; status: number } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFailed\n \"Network.loadingFailed\": { requestId: string; errorText: string; canceled?: boolean };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFinished\n \"Network.loadingFinished\": { requestId: string };\n};\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\nexport class CdpConnection {\n private sequence = 0;\n private pending = new Map<number, Pending>();\n private listeners = new Map<string, Set<(params: unknown) => void>>();\n private closed = false;\n\n private constructor(private socket: WebSocket) {\n socket.addEventListener(\"message\", ({ data }) => {\n try {\n const message = JSON.parse(String(data)) as {\n id?: number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { message: string };\n };\n if (typeof message.id === \"number\") {\n const pending = this.pending.get(message.id);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(message.id);\n if (message.error) pending.reject(new Error(`CDP: ${message.error.message}`));\n else pending.resolve(message.result);\n } else if (message.method) {\n for (const listener of this.listeners.get(message.method) ?? []) listener(message.params);\n }\n } catch (error) {\n this.fail(new Error(`Invalid CDP message: ${String(error)}`));\n }\n });\n socket.addEventListener(\"close\", () => this.fail(new Error(\"CDP connection closed\")));\n socket.addEventListener(\"error\", () => this.fail(new Error(\"CDP WebSocket error\")));\n }\n\n static async connect(url: string, timeoutMs = 10_000): Promise<CdpConnection> {\n // Stable since Node 22.4, present in the minimum supported Node 22.15.\n // https://nodejs.org/download/release/v22.15.0/docs/api/globals.html#websocket\n if (typeof WebSocket !== \"function\")\n throw new Error(\n \"Browser e2e requires Node's global WebSocket (Node >=22.15, without --no-experimental-websocket)\",\n );\n const socket = new WebSocket(url);\n const connection = new CdpConnection(socket);\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n socket.removeEventListener(\"open\", opened);\n socket.removeEventListener(\"error\", failed);\n socket.removeEventListener(\"close\", failed);\n };\n const opened = () => {\n cleanup();\n resolve();\n };\n const failed = () => {\n cleanup();\n socket.close();\n reject(new Error(`Cannot connect to owned browser CDP endpoint ${url}`));\n };\n const timer = setTimeout(failed, timeoutMs);\n socket.addEventListener(\"open\", opened, { once: true });\n socket.addEventListener(\"error\", failed, { once: true });\n socket.addEventListener(\"close\", failed, { once: true });\n });\n return connection;\n }\n\n command<K extends keyof CdpMethods>(\n method: K,\n params: CdpMethods[K][\"params\"],\n timeoutMs = 10_000,\n ): Promise<CdpMethods[K][\"result\"]> {\n if (this.closed) return Promise.reject(new Error(`CDP closed before ${method}`));\n return new Promise<CdpMethods[K][\"result\"]>((resolve, reject) => {\n const id = ++this.sequence;\n const timer = setTimeout(\n () => {\n this.pending.delete(id);\n reject(new Error(`CDP timeout after ${timeoutMs}ms: ${method}`));\n },\n Math.max(1, timeoutMs),\n );\n this.pending.set(id, {\n resolve: (value) => resolve(value as CdpMethods[K][\"result\"]),\n reject,\n timer,\n });\n try {\n this.socket.send(JSON.stringify({ id, method, params }));\n } catch (error) {\n clearTimeout(timer);\n this.pending.delete(id);\n reject(error);\n }\n });\n }\n\n on<K extends keyof CdpEvents>(method: K, listener: (params: CdpEvents[K]) => void): () => void {\n const wrapped = (params: unknown) => listener(params as CdpEvents[K]);\n const listeners = this.listeners.get(method) ?? new Set();\n listeners.add(wrapped);\n this.listeners.set(method, listeners);\n return () => {\n listeners.delete(wrapped);\n };\n }\n\n private fail(error: Error): void {\n this.closed = true;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n close(): void {\n this.fail(new Error(\"CDP connection closed by harness\"));\n this.listeners.clear();\n this.socket.close();\n }\n}\n"],"mappings":";AA6EA,IAAa,gBAAb,MAAa,cAAc;CAMzB,AAAQ,YAAY,AAAQ,QAAmB;EAAnB;kBALT;iCACD,IAAI,IAAqB;mCACvB,IAAI,IAA4C;gBACnD;EAGf,OAAO,iBAAiB,YAAY,EAAE,WAAW;GAC/C,IAAI;IACF,MAAM,UAAU,KAAK,MAAM,OAAO,IAAI,CAAC;IAOvC,IAAI,OAAO,QAAQ,OAAO,UAAU;KAClC,MAAM,UAAU,KAAK,QAAQ,IAAI,QAAQ,EAAE;KAC3C,IAAI,CAAC,SAAS;KACd,aAAa,QAAQ,KAAK;KAC1B,KAAK,QAAQ,OAAO,QAAQ,EAAE;KAC9B,IAAI,QAAQ,OAAO,QAAQ,uBAAO,IAAI,MAAM,QAAQ,QAAQ,MAAM,SAAS,CAAC;UACvE,QAAQ,QAAQ,QAAQ,MAAM;IACrC,OAAO,IAAI,QAAQ,QACjB,KAAK,MAAM,YAAY,KAAK,UAAU,IAAI,QAAQ,MAAM,KAAK,CAAC,GAAG,SAAS,QAAQ,MAAM;GAE5F,SAAS,OAAO;IACd,KAAK,qBAAK,IAAI,MAAM,wBAAwB,OAAO,KAAK,GAAG,CAAC;GAC9D;EACF,CAAC;EACD,OAAO,iBAAiB,eAAe,KAAK,qBAAK,IAAI,MAAM,uBAAuB,CAAC,CAAC;EACpF,OAAO,iBAAiB,eAAe,KAAK,qBAAK,IAAI,MAAM,qBAAqB,CAAC,CAAC;CACpF;CAEA,aAAa,QAAQ,KAAa,YAAY,KAAgC;EAG5E,IAAI,OAAO,cAAc,YACvB,MAAM,IAAI,MACR,kGACF;EACF,MAAM,SAAS,IAAI,UAAU,GAAG;EAChC,MAAM,aAAa,IAAI,cAAc,MAAM;EAC3C,MAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,MAAM,gBAAgB;IACpB,aAAa,KAAK;IAClB,OAAO,oBAAoB,QAAQ,MAAM;IACzC,OAAO,oBAAoB,SAAS,MAAM;IAC1C,OAAO,oBAAoB,SAAS,MAAM;GAC5C;GACA,MAAM,eAAe;IACnB,QAAQ;IACR,QAAQ;GACV;GACA,MAAM,eAAe;IACnB,QAAQ;IACR,OAAO,MAAM;IACb,uBAAO,IAAI,MAAM,gDAAgD,KAAK,CAAC;GACzE;GACA,MAAM,QAAQ,WAAW,QAAQ,SAAS;GAC1C,OAAO,iBAAiB,QAAQ,QAAQ,EAAE,MAAM,KAAK,CAAC;GACtD,OAAO,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;GACvD,OAAO,iBAAiB,SAAS,QAAQ,EAAE,MAAM,KAAK,CAAC;EACzD,CAAC;EACD,OAAO;CACT;CAEA,QACE,QACA,QACA,YAAY,KACsB;EAClC,IAAI,KAAK,QAAQ,OAAO,QAAQ,uBAAO,IAAI,MAAM,qBAAqB,QAAQ,CAAC;EAC/E,OAAO,IAAI,SAAkC,SAAS,WAAW;GAC/D,MAAM,KAAK,EAAE,KAAK;GAClB,MAAM,QAAQ,iBACN;IACJ,KAAK,QAAQ,OAAO,EAAE;IACtB,uBAAO,IAAI,MAAM,qBAAqB,UAAU,MAAM,QAAQ,CAAC;GACjE,GACA,KAAK,IAAI,GAAG,SAAS,CACvB;GACA,KAAK,QAAQ,IAAI,IAAI;IACnB,UAAU,UAAU,QAAQ,KAAgC;IAC5D;IACA;GACF,CAAC;GACD,IAAI;IACF,KAAK,OAAO,KAAK,KAAK,UAAU;KAAE;KAAI;KAAQ;IAAO,CAAC,CAAC;GACzD,SAAS,OAAO;IACd,aAAa,KAAK;IAClB,KAAK,QAAQ,OAAO,EAAE;IACtB,OAAO,KAAK;GACd;EACF,CAAC;CACH;CAEA,GAA8B,QAAW,UAAsD;EAC7F,MAAM,WAAW,WAAoB,SAAS,MAAsB;EACpE,MAAM,YAAY,KAAK,UAAU,IAAI,MAAM,qBAAK,IAAI,IAAI;EACxD,UAAU,IAAI,OAAO;EACrB,KAAK,UAAU,IAAI,QAAQ,SAAS;EACpC,aAAa;GACX,UAAU,OAAO,OAAO;EAC1B;CACF;CAEA,AAAQ,KAAK,OAAoB;EAC/B,KAAK,SAAS;EACd,KAAK,MAAM,WAAW,KAAK,QAAQ,OAAO,GAAG;GAC3C,aAAa,QAAQ,KAAK;GAC1B,QAAQ,OAAO,KAAK;EACtB;EACA,KAAK,QAAQ,MAAM;CACrB;CAEA,QAAc;EACZ,KAAK,qBAAK,IAAI,MAAM,kCAAkC,CAAC;EACvD,KAAK,UAAU,MAAM;EACrB,KAAK,OAAO,MAAM;CACpB;AACF"}
|
|
1
|
+
{"version":3,"file":"cdp.mjs","names":[],"sources":["../../../../../../../../packages/core/src/e2e/cdp.ts"],"sourcesContent":["/** Wire shapes verified against the official protocol viewer and its source JSON:\n * https://chromedevtools.github.io/devtools-protocol/\n * https://github.com/ChromeDevTools/devtools-protocol/tree/master/json\n * Only the fields consumed by this harness are modeled here.\n */\ntype RemoteObject = { value?: unknown; description?: string };\ntype ExceptionDetails = { text: string; exception?: RemoteObject };\nexport type CdpMethods = {\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable\n \"Runtime.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate\n \"Runtime.evaluate\": {\n params: { expression: string; returnByValue: boolean; awaitPromise: boolean; silent: boolean };\n result: { result: RemoteObject; exceptionDetails?: ExceptionDetails };\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-enable\n \"Page.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-navigate\n \"Page.navigate\": {\n params: { url: string };\n result: { frameId: string; loaderId?: string; errorText?: string };\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot\n \"Page.captureScreenshot\": { params: { format: \"png\" }; result: { data: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride\n \"Emulation.setDeviceMetricsOverride\": {\n params: { width: number; height: number; deviceScaleFactor: number; mobile: boolean };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent\n \"Input.dispatchMouseEvent\": {\n params: {\n type: \"mouseMoved\" | \"mousePressed\" | \"mouseReleased\";\n x: number;\n y: number;\n button?: \"left\";\n buttons?: number;\n clickCount?: number;\n };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-insertText\n \"Input.insertText\": { params: { text: string }; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent\n \"Input.dispatchKeyEvent\": {\n params: { type: \"keyDown\" | \"keyUp\"; key: string; code: string; windowsVirtualKeyCode: number };\n result: Record<string, never>;\n };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-enable\n \"Network.enable\": { params: Record<string, never>; result: Record<string, never> };\n // https://chromedevtools.github.io/devtools-protocol/tot/Log/#method-enable\n \"Log.enable\": { params: Record<string, never>; result: Record<string, never> };\n};\nexport type CdpEvents = {\n // https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-loadEventFired\n \"Page.loadEventFired\": { timestamp: number };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-consoleAPICalled\n \"Runtime.consoleAPICalled\": { type: string; args: RemoteObject[] };\n // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-exceptionThrown\n \"Runtime.exceptionThrown\": { exceptionDetails: ExceptionDetails };\n // https://chromedevtools.github.io/devtools-protocol/tot/Log/#event-entryAdded\n \"Log.entryAdded\": { entry: { level: string; text: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-requestWillBeSent\n \"Network.requestWillBeSent\": { requestId: string; request: { url: string } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-responseReceived\n \"Network.responseReceived\": { requestId: string; response: { url: string; status: number } };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFailed\n \"Network.loadingFailed\": { requestId: string; errorText: string; canceled?: boolean };\n // https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-loadingFinished\n \"Network.loadingFinished\": { requestId: string };\n};\ntype Pending = {\n resolve: (value: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/** A command got no reply within its budget. Retry loops that shrink their budget use this type to tell a timeout apart from a reply that failed. */\nexport class CdpTimeoutError extends Error {\n constructor(timeoutMs: number, method: string) {\n super(`CDP timeout after ${timeoutMs}ms: ${method}`);\n this.name = \"CdpTimeoutError\";\n }\n}\n\nexport class CdpConnection {\n private sequence = 0;\n private pending = new Map<number, Pending>();\n private listeners = new Map<string, Set<(params: unknown) => void>>();\n private closed = false;\n\n private constructor(private socket: WebSocket) {\n socket.addEventListener(\"message\", ({ data }) => {\n try {\n const message = JSON.parse(String(data)) as {\n id?: number;\n method?: string;\n params?: unknown;\n result?: unknown;\n error?: { message: string };\n };\n if (typeof message.id === \"number\") {\n const pending = this.pending.get(message.id);\n if (!pending) return;\n clearTimeout(pending.timer);\n this.pending.delete(message.id);\n if (message.error) pending.reject(new Error(`CDP: ${message.error.message}`));\n else pending.resolve(message.result);\n } else if (message.method) {\n for (const listener of this.listeners.get(message.method) ?? []) listener(message.params);\n }\n } catch (error) {\n this.fail(new Error(`Invalid CDP message: ${String(error)}`));\n }\n });\n socket.addEventListener(\"close\", () => this.fail(new Error(\"CDP connection closed\")));\n socket.addEventListener(\"error\", () => this.fail(new Error(\"CDP WebSocket error\")));\n }\n\n static async connect(url: string, timeoutMs = 10_000): Promise<CdpConnection> {\n // Stable since Node 22.4, present in the minimum supported Node 22.15.\n // https://nodejs.org/download/release/v22.15.0/docs/api/globals.html#websocket\n if (typeof WebSocket !== \"function\")\n throw new Error(\n \"Browser e2e requires Node's global WebSocket (Node >=22.15, without --no-experimental-websocket)\",\n );\n const socket = new WebSocket(url);\n const connection = new CdpConnection(socket);\n await new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n clearTimeout(timer);\n socket.removeEventListener(\"open\", opened);\n socket.removeEventListener(\"error\", failed);\n socket.removeEventListener(\"close\", failed);\n };\n const opened = () => {\n cleanup();\n resolve();\n };\n const failed = () => {\n cleanup();\n socket.close();\n reject(new Error(`Cannot connect to owned browser CDP endpoint ${url}`));\n };\n const timer = setTimeout(failed, timeoutMs);\n socket.addEventListener(\"open\", opened, { once: true });\n socket.addEventListener(\"error\", failed, { once: true });\n socket.addEventListener(\"close\", failed, { once: true });\n });\n return connection;\n }\n\n command<K extends keyof CdpMethods>(\n method: K,\n params: CdpMethods[K][\"params\"],\n timeoutMs = 10_000,\n ): Promise<CdpMethods[K][\"result\"]> {\n if (this.closed) return Promise.reject(new Error(`CDP closed before ${method}`));\n return new Promise<CdpMethods[K][\"result\"]>((resolve, reject) => {\n const id = ++this.sequence;\n const timer = setTimeout(\n () => {\n this.pending.delete(id);\n reject(new CdpTimeoutError(timeoutMs, method));\n },\n Math.max(1, timeoutMs),\n );\n this.pending.set(id, {\n resolve: (value) => resolve(value as CdpMethods[K][\"result\"]),\n reject,\n timer,\n });\n try {\n this.socket.send(JSON.stringify({ id, method, params }));\n } catch (error) {\n clearTimeout(timer);\n this.pending.delete(id);\n reject(error);\n }\n });\n }\n\n on<K extends keyof CdpEvents>(method: K, listener: (params: CdpEvents[K]) => void): () => void {\n const wrapped = (params: unknown) => listener(params as CdpEvents[K]);\n const listeners = this.listeners.get(method) ?? new Set();\n listeners.add(wrapped);\n this.listeners.set(method, listeners);\n return () => {\n listeners.delete(wrapped);\n };\n }\n\n private fail(error: Error): void {\n this.closed = true;\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timer);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n close(): void {\n this.fail(new Error(\"CDP connection closed by harness\"));\n this.listeners.clear();\n this.socket.close();\n }\n}\n"],"mappings":"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"}
|
package/esm/e2e/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BrowserPage } from "./page.mjs";
|
|
2
|
-
import { AppFixtureOptions, BrowserOptions, Command, ConsoleEntry, E2EContext, E2EEvidence, E2ESpec, FixtureContext, NetworkAllowance, NetworkFailure, Pattern, ProcessEvidence, RunE2EOptions, StepEvidence, Viewport, defaultViewports, defineE2E } from "./types.mjs";
|
|
2
|
+
import { AccessibilityEvidence, AccessibilityOptions, AccessibilityViolation, AppFixtureOptions, BrowserOptions, Command, ConsoleEntry, E2EContext, E2EEvidence, E2ESpec, FixtureContext, NetworkAllowance, NetworkFailure, Pattern, ProcessEvidence, RunE2EOptions, StepEvidence, Viewport, defaultViewports, defineE2E } from "./types.mjs";
|
|
3
3
|
import { findBrowserBinary } from "./browser.mjs";
|
|
4
4
|
import { E2EResult, E2ERunError, runE2E } from "./run-e2e.mjs";
|
|
5
|
-
export { type AppFixtureOptions, type BrowserOptions, BrowserPage, type Command, type ConsoleEntry, type E2EContext, type E2EEvidence, type E2EResult, E2ERunError, type E2ESpec, type FixtureContext, type NetworkAllowance, type NetworkFailure, type Pattern, type ProcessEvidence, type RunE2EOptions, type StepEvidence, type Viewport, defaultViewports, defineE2E, findBrowserBinary, runE2E };
|
|
5
|
+
export { type AccessibilityEvidence, type AccessibilityOptions, type AccessibilityViolation, type AppFixtureOptions, type BrowserOptions, BrowserPage, type Command, type ConsoleEntry, type E2EContext, type E2EEvidence, type E2EResult, E2ERunError, type E2ESpec, type FixtureContext, type NetworkAllowance, type NetworkFailure, type Pattern, type ProcessEvidence, type RunE2EOptions, type StepEvidence, type Viewport, defaultViewports, defineE2E, findBrowserBinary, runE2E };
|
package/esm/e2e/page.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CdpConnection } from "./cdp.mjs";
|
|
2
|
-
import { E2EEvidence, E2ESpec, Viewport } from "./types.mjs";
|
|
2
|
+
import { AccessibilityEvidence, AccessibilityOptions, E2EEvidence, E2ESpec, PayloadEvidence, Viewport } from "./types.mjs";
|
|
3
3
|
//#region ../../packages/core/src/e2e/page.d.ts
|
|
4
4
|
type WaitOptions = {
|
|
5
5
|
selector?: string;
|
|
@@ -53,6 +53,19 @@ export declare class BrowserPage {
|
|
|
53
53
|
/** Appends Unicode text through the browser's text-input path. */
|
|
54
54
|
type(selector: string, text: string): Promise<void>;
|
|
55
55
|
setViewport(viewport: Viewport): Promise<void>;
|
|
56
|
+
/** Inject the installed axe distribution through Runtime.evaluate on every scan.
|
|
57
|
+
* API: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axerun
|
|
58
|
+
* No CDN, DOM script element, or application dependency is required.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* Measures the inline payload of the document the page currently shows and
|
|
62
|
+
* records it under `evidence.payloads`. Measure after a full load: a client
|
|
63
|
+
* navigation replaces the data it reads. Pass `limit` to mark an overage.
|
|
64
|
+
*/
|
|
65
|
+
measurePayload(options?: {
|
|
66
|
+
limit?: number;
|
|
67
|
+
}): Promise<PayloadEvidence>;
|
|
68
|
+
assertAccessible(options?: AccessibilityOptions): Promise<AccessibilityEvidence>;
|
|
56
69
|
assertViewport(): Promise<ViewportReport>;
|
|
57
70
|
screenshot(name: string): Promise<string>;
|
|
58
71
|
captureViewports(name: string, viewports?: readonly Viewport[]): Promise<void>;
|
package/esm/e2e/page.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/page.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"page.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/page.ts"],"mappings":";;;KAiBK;EAAgB;EAAmB;EAAe;EAAc;;KAEhE;EACH;EACA;EACA;EACA;EACA;IACE;IACA;IACA;IACA;IACA;IACA;IACA;;;qBAgES;UAQD;UACA;UACA;UACA;UACA;UAXF;UACA;UACA;UACA;UACA;EAER,YACU,KAAK,eACL,gBACA,yBACA,UAAU,aACV,MAAM;UAgDR;EAMF,cAAc;EAQd,KAAK,GAAG,cAAc,cAAc,QAAQ,KAAK,QAAQ;EAqBzD,SAAS,aAAa,oBAAoB,qBAAqB,QAAQ;UAa/D;EAoBR,KAAK,cAAc,qBAAqB;EAkBxC,QAAQ,SAAS,cAAc;;;;EA0B/B,kBACJ,cAAc,kBACd;IAAW;IAAc;MACxB;EAkBG,MAAM,kBAAkB,qBAAqB;EA6E7C,KAAK,kBAAkB,gBAAgB;;EA4BvC,KAAK,kBAAkB,eAAe;EA0BtC,YAAY,UAAU,WAAW;;;;;;;;;;EAgCjC,eAAe;IAAW;MAAwB,QAAQ;EA2B1D,iBAAiB,UAAS,uBAA4B,QAAQ;EA8C9D,kBAAkB,QAAQ;EAoB1B,WAAW,eAAe;EAY1B,iBACJ,cACA,qBAAS,aACR;EAuBG,mBAAmB,qBAAqB;EAgB9C"}
|
package/esm/e2e/page.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { CdpTimeoutError } from "./cdp.mjs";
|
|
1
2
|
import { defaultViewports, errorMessage, matches } from "./types.mjs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
2
4
|
import { join } from "node:path";
|
|
3
|
-
import { writeFile } from "node:fs/promises";
|
|
5
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4
6
|
import { setTimeout } from "node:timers/promises";
|
|
5
7
|
|
|
6
8
|
//#region ../../packages/core/src/e2e/page.ts
|
|
@@ -54,6 +56,14 @@ function boxesStable(first, second) {
|
|
|
54
56
|
"height"
|
|
55
57
|
].every((key) => Math.abs(first[key] - second[key]) < .25);
|
|
56
58
|
}
|
|
59
|
+
/** Reason to report after a failed probe in a retry loop. A CDP timeout comes from
|
|
60
|
+
* the deadline shrinking under the probe, not from the target, so it must not
|
|
61
|
+
* replace a reason already observed on the target. Any other error is reported.
|
|
62
|
+
*/
|
|
63
|
+
function probeReason(error, observed) {
|
|
64
|
+
if (error instanceof CdpTimeoutError && observed !== void 0) return observed;
|
|
65
|
+
return errorMessage(error);
|
|
66
|
+
}
|
|
57
67
|
var BrowserPage = class {
|
|
58
68
|
constructor(cdp, origin, outputDirectory, evidence, spec) {
|
|
59
69
|
this.cdp = cdp;
|
|
@@ -152,16 +162,16 @@ var BrowserPage = class {
|
|
|
152
162
|
}
|
|
153
163
|
async poll(description, check, timeoutMs) {
|
|
154
164
|
const deadline = Date.now() + timeoutMs;
|
|
155
|
-
let reason
|
|
165
|
+
let reason;
|
|
156
166
|
while (Date.now() < deadline) {
|
|
157
167
|
try {
|
|
158
168
|
if (await check(Math.max(1, deadline - Date.now()))) return;
|
|
159
169
|
} catch (error) {
|
|
160
|
-
reason =
|
|
170
|
+
reason = probeReason(error, reason);
|
|
161
171
|
}
|
|
162
172
|
await setTimeout(Math.min(50, Math.max(0, deadline - Date.now())));
|
|
163
173
|
}
|
|
164
|
-
throw new Error(`${description} timed out after ${timeoutMs}ms: ${reason}`);
|
|
174
|
+
throw new Error(`${description} timed out after ${timeoutMs}ms: ${reason ?? "condition stayed false"}`);
|
|
165
175
|
}
|
|
166
176
|
async goto(path, timeoutMs = 1e4) {
|
|
167
177
|
await this.step(`goto ${path}`, async () => {
|
|
@@ -197,14 +207,14 @@ var BrowserPage = class {
|
|
|
197
207
|
await this.step(`click ${selector}`, async () => {
|
|
198
208
|
const deadline = Date.now() + timeoutMs;
|
|
199
209
|
let previous;
|
|
200
|
-
let reason
|
|
210
|
+
let reason;
|
|
201
211
|
while (Date.now() < deadline) {
|
|
202
212
|
const remaining = () => Math.max(1, deadline - Date.now());
|
|
203
213
|
let box;
|
|
204
214
|
try {
|
|
205
215
|
box = await this.evaluate(`(${clickProbe.toString()})(${JSON.stringify(selector)})`, remaining());
|
|
206
216
|
} catch (error) {
|
|
207
|
-
reason =
|
|
217
|
+
reason = probeReason(error, reason);
|
|
208
218
|
previous = void 0;
|
|
209
219
|
await setTimeout(25);
|
|
210
220
|
continue;
|
|
@@ -220,7 +230,7 @@ var BrowserPage = class {
|
|
|
220
230
|
}, remaining());
|
|
221
231
|
hovered = await this.evaluate(`(${clickProbe.toString()})(${JSON.stringify(selector)})`, remaining());
|
|
222
232
|
} catch (error) {
|
|
223
|
-
reason =
|
|
233
|
+
reason = probeReason(error, reason);
|
|
224
234
|
previous = void 0;
|
|
225
235
|
await setTimeout(Math.min(25, remaining()));
|
|
226
236
|
continue;
|
|
@@ -249,7 +259,7 @@ var BrowserPage = class {
|
|
|
249
259
|
} else previous = box;
|
|
250
260
|
await setTimeout(Math.min(50, remaining()));
|
|
251
261
|
}
|
|
252
|
-
throw new Error(`Cannot click ${JSON.stringify(selector)} within ${timeoutMs}ms: ${reason}`);
|
|
262
|
+
throw new Error(`Cannot click ${JSON.stringify(selector)} within ${timeoutMs}ms: ${reason ?? "layout has not settled"}`);
|
|
253
263
|
});
|
|
254
264
|
}
|
|
255
265
|
async fill(selector, value) {
|
|
@@ -306,6 +316,69 @@ var BrowserPage = class {
|
|
|
306
316
|
await this.evaluate("new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve(true))))");
|
|
307
317
|
});
|
|
308
318
|
}
|
|
319
|
+
/** Inject the installed axe distribution through Runtime.evaluate on every scan.
|
|
320
|
+
* API: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axerun
|
|
321
|
+
* No CDN, DOM script element, or application dependency is required.
|
|
322
|
+
*/
|
|
323
|
+
/**
|
|
324
|
+
* Measures the inline payload of the document the page currently shows and
|
|
325
|
+
* records it under `evidence.payloads`. Measure after a full load: a client
|
|
326
|
+
* navigation replaces the data it reads. Pass `limit` to mark an overage.
|
|
327
|
+
*/
|
|
328
|
+
async measurePayload(options = {}) {
|
|
329
|
+
return this.step(`measurePayload ${this.viewport.name}`, async () => {
|
|
330
|
+
const measured = await this.evaluate(`(() => {
|
|
331
|
+
const bytes = (id) => {
|
|
332
|
+
const node = document.getElementById(id);
|
|
333
|
+
return node === null ? 0 : new TextEncoder().encode(node.textContent || "").length;
|
|
334
|
+
};
|
|
335
|
+
return { url: location.href, data: bytes("__WARLOCK_DATA__"), routes: bytes("__WARLOCK_NAMED_API_ROUTES__") };
|
|
336
|
+
})()`, 1e4);
|
|
337
|
+
const total = measured.data + measured.routes;
|
|
338
|
+
const report = {
|
|
339
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
340
|
+
viewport: { ...this.viewport },
|
|
341
|
+
...measured,
|
|
342
|
+
total,
|
|
343
|
+
...options.limit === void 0 ? {} : {
|
|
344
|
+
limit: options.limit,
|
|
345
|
+
overBudget: total > options.limit
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
(this.evidence.payloads ??= []).push(report);
|
|
349
|
+
return report;
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
async assertAccessible(options = {}) {
|
|
353
|
+
return this.step(`assertAccessible ${this.viewport.name}`, async () => {
|
|
354
|
+
const source = await readFile(createRequire(import.meta.url).resolve("axe-core/axe.min.js"), "utf8");
|
|
355
|
+
const timeout = options.timeoutMs ?? 3e4;
|
|
356
|
+
await this.evaluate(source, timeout);
|
|
357
|
+
const result = await this.evaluate(`(async () => {
|
|
358
|
+
const selector = ${JSON.stringify(options.selector ?? null)};
|
|
359
|
+
const context = selector === null ? document : document.querySelector(selector);
|
|
360
|
+
if (!context) throw new Error('Accessibility scan selector did not match: ' + selector);
|
|
361
|
+
const results = await axe.run(context, ${JSON.stringify(options.tags ? { runOnly: {
|
|
362
|
+
type: "tag",
|
|
363
|
+
values: options.tags
|
|
364
|
+
} } : {})});
|
|
365
|
+
const summarize = entries => entries.map(({id, impact, help, helpUrl, nodes}) => ({
|
|
366
|
+
id, impact, help, helpUrl, nodes: nodes.map(({target, failureSummary}) => ({target, failureSummary}))
|
|
367
|
+
}));
|
|
368
|
+
return {url: location.href, violations: summarize(results.violations), incomplete: summarize(results.incomplete)};
|
|
369
|
+
})()`, timeout);
|
|
370
|
+
for (const violation of result.violations) for (const node of violation.nodes) node.allowed = options.allow?.some((rule) => rule.id === violation.id && (rule.selector === void 0 || node.target.length === 1 && node.target[0] === rule.selector)) ?? false;
|
|
371
|
+
const report = {
|
|
372
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
373
|
+
viewport: { ...this.viewport },
|
|
374
|
+
...result
|
|
375
|
+
};
|
|
376
|
+
(this.evidence.accessibility ??= []).push(report);
|
|
377
|
+
const failures = report.violations.flatMap((violation) => violation.nodes.filter((node) => !node.allowed).map((node) => `${violation.id}: ${JSON.stringify(node.target)} — ${violation.help}`));
|
|
378
|
+
if (failures.length) throw new Error(`Accessibility violations:\n${failures.join("\n")}`);
|
|
379
|
+
return report;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
309
382
|
async assertViewport() {
|
|
310
383
|
return this.step(`assertViewport ${this.viewport.name}`, async () => {
|
|
311
384
|
const report = await this.evaluate(`({
|
package/esm/e2e/page.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.mjs","names":["delay"],"sources":["../../../../../../../../packages/core/src/e2e/page.ts"],"sourcesContent":["import { writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { CdpConnection } from \"./cdp\";\nimport {\n defaultViewports,\n errorMessage,\n matches,\n type E2EEvidence,\n type E2ESpec,\n type Viewport,\n} from \"./types\";\n\ntype WaitOptions = { selector?: string; text?: string; url?: string; timeoutMs?: number };\ntype ClickBox = { x: number; y: number; width: number; height: number; reason: string };\ntype ViewportReport = {\n requestedWidth: number;\n innerWidth: number;\n documentWidth: number;\n bodyWidth: number;\n offenders: {\n tag: string;\n id: string;\n className: string;\n width: number;\n scrollWidth: number;\n right: number;\n left: number;\n }[];\n};\n\n/** Runs inside the browser. Two paints plus cross-probe box comparison detect\n * scroll/animation/layout races. The hit test also accepts target descendants.\n */\nfunction clickProbe(selector: string): Promise<ClickBox> {\n return (async () => {\n const empty = (reason: string): ClickBox => ({ x: 0, y: 0, width: 0, height: 0, reason });\n const element = document.querySelector(selector);\n if (!(element instanceof HTMLElement || element instanceof SVGElement))\n return empty(\"target is missing or is not an HTML/SVG element\");\n element.scrollIntoView({ block: \"center\", inline: \"center\", behavior: \"instant\" });\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n );\n if (!element.isConnected || document.querySelector(selector) !== element)\n return empty(\"target was replaced during layout\");\n const box = element.getBoundingClientRect();\n const x = box.x + box.width / 2;\n const y = box.y + box.height / 2;\n const style = getComputedStyle(element);\n let reason = \"\";\n if (\n box.width <= 0 ||\n box.height <= 0 ||\n style.visibility !== \"visible\" ||\n style.display === \"none\" ||\n Number(style.opacity) === 0\n )\n reason = \"target is hidden or has an empty box\";\n else if (element.matches(\":disabled\") || element.closest('[inert], [aria-disabled=\"true\"]'))\n reason = \"target is disabled or inert\";\n else if (x < 0 || y < 0 || x >= innerWidth || y >= innerHeight)\n reason = \"target center is outside the viewport\";\n else {\n const top = document.elementFromPoint(x, y);\n if (!top || !element.contains(top))\n reason = `target is covered by ${top?.tagName ?? \"no element\"}${top?.id ? \"#\" + top.id : \"\"}`;\n }\n return { x, y, width: box.width, height: box.height, reason };\n })();\n}\n\nexport function boxesStable(first: ClickBox, second: ClickBox): boolean {\n return (\n !first.reason &&\n !second.reason &&\n ([\"x\", \"y\", \"width\", \"height\"] as const).every(\n (key) => Math.abs(first[key] - second[key]) < 0.25,\n )\n );\n}\n\nexport class BrowserPage {\n private viewport: Viewport = { ...defaultViewports[0] };\n private loads = 0;\n private pendingRequests = new Map<string, string>();\n private requestUrls = new Map<string, string>();\n private lastNetworkActivity = Date.now();\n\n constructor(\n private cdp: CdpConnection,\n private origin: string,\n private outputDirectory: string,\n private evidence: E2EEvidence,\n private spec: E2ESpec,\n ) {\n cdp.on(\"Page.loadEventFired\", () => {\n this.loads++;\n });\n cdp.on(\"Runtime.consoleAPICalled\", ({ type, args }) =>\n evidence.console.push({\n at: new Date().toISOString(),\n level: type === \"assert\" ? \"error\" : type,\n text: args.map((arg) => String(arg.value ?? arg.description ?? \"\")).join(\" \"),\n }),\n );\n cdp.on(\"Runtime.exceptionThrown\", ({ exceptionDetails }) =>\n evidence.console.push({\n at: new Date().toISOString(),\n level: \"error\",\n text: exceptionDetails.exception?.description ?? exceptionDetails.text,\n }),\n );\n cdp.on(\"Log.entryAdded\", ({ entry }) =>\n evidence.console.push({ at: new Date().toISOString(), level: entry.level, text: entry.text }),\n );\n cdp.on(\"Network.requestWillBeSent\", ({ requestId, request }) => {\n this.pendingRequests.set(requestId, request.url);\n this.requestUrls.set(requestId, request.url);\n this.lastNetworkActivity = Date.now();\n });\n cdp.on(\"Network.responseReceived\", ({ requestId, response }) => {\n this.requestUrls.set(requestId, response.url);\n if (response.status >= 400)\n evidence.networkFailures.push({\n at: new Date().toISOString(),\n url: response.url,\n status: response.status,\n });\n });\n cdp.on(\"Network.loadingFailed\", ({ requestId, errorText, canceled }) => {\n evidence.networkFailures.push({\n at: new Date().toISOString(),\n url: this.requestUrls.get(requestId) ?? `unknown request ${requestId}`,\n errorText,\n canceled,\n });\n this.completeRequest(requestId);\n });\n cdp.on(\"Network.loadingFinished\", ({ requestId }) => this.completeRequest(requestId));\n }\n\n private completeRequest(requestId: string): void {\n this.pendingRequests.delete(requestId);\n this.requestUrls.delete(requestId);\n this.lastNetworkActivity = Date.now();\n }\n\n async initialize(): Promise<void> {\n await this.cdp.command(\"Page.enable\", {});\n await this.cdp.command(\"Runtime.enable\", {});\n await this.cdp.command(\"Network.enable\", {});\n await this.cdp.command(\"Log.enable\", {});\n await this.setViewport(this.spec.viewports?.[0] ?? defaultViewports[0]);\n }\n\n async step<T>(name: string, action: () => Promise<T>): Promise<T> {\n const start = performance.now();\n const entry = {\n name,\n startedAt: new Date().toISOString(),\n durationMs: 0,\n status: \"passed\" as \"passed\" | \"failed\",\n error: undefined as string | undefined,\n };\n this.evidence.steps.push(entry);\n try {\n return await action();\n } catch (error) {\n entry.status = \"failed\";\n entry.error = errorMessage(error);\n throw error;\n } finally {\n entry.durationMs = Math.round(performance.now() - start);\n }\n }\n\n async evaluate<T = unknown>(expression: string, timeoutMs = 10_000): Promise<T> {\n const response = await this.cdp.command(\n \"Runtime.evaluate\",\n { expression, returnByValue: true, awaitPromise: true, silent: true },\n timeoutMs,\n );\n if (response.exceptionDetails)\n throw new Error(\n `Browser evaluation failed: ${response.exceptionDetails.exception?.description ?? response.exceptionDetails.text}`,\n );\n return response.result.value as T;\n }\n\n private async poll(\n description: string,\n check: (remainingMs: number) => Promise<boolean>,\n timeoutMs: number,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n let reason = \"condition stayed false\";\n while (Date.now() < deadline) {\n try {\n if (await check(Math.max(1, deadline - Date.now()))) return;\n } catch (error) {\n reason = errorMessage(error);\n }\n await delay(Math.min(50, Math.max(0, deadline - Date.now())));\n }\n throw new Error(`${description} timed out after ${timeoutMs}ms: ${reason}`);\n }\n\n async goto(path: string, timeoutMs = 10_000): Promise<void> {\n await this.step(`goto ${path}`, async () => {\n const url = new URL(path, this.origin).href;\n const before = this.loads;\n const navigation = await this.cdp.command(\"Page.navigate\", { url }, timeoutMs);\n if (navigation.errorText) throw new Error(`Navigation failed: ${navigation.errorText}`);\n await this.poll(\n `Navigation to ${url}`,\n (remaining) =>\n this.evaluate<boolean>(\n `document.readyState === 'complete' && ${navigation.loaderId ? this.loads > before : \"true\"}`,\n remaining,\n ),\n timeoutMs,\n );\n });\n }\n\n async waitFor(options: WaitOptions): Promise<void> {\n if (options.selector === undefined && options.text === undefined && options.url === undefined)\n throw new Error(\"waitFor requires a selector, text, or URL\");\n await this.step(`waitFor ${JSON.stringify(options)}`, () =>\n this.poll(\n \"waitFor \" + JSON.stringify(options),\n (remaining) =>\n this.evaluate<boolean>(\n `(() => {\n const options = ${JSON.stringify(options)};\n const element = options.selector ? document.querySelector(options.selector) : document.body;\n if (!element) return false;\n if (options.selector) { const box=element.getBoundingClientRect(); const css=getComputedStyle(element); if (!box.width || !box.height || css.visibility !== 'visible') return false; }\n return (options.text === undefined || (element.innerText ?? element.textContent ?? '').includes(options.text)) &&\n (options.url === undefined || location.href === new URL(options.url, ${JSON.stringify(this.origin)}).href);\n })()`,\n remaining,\n ),\n options.timeoutMs ?? 10_000,\n ),\n );\n }\n\n /** Arms the document/URL comparison BEFORE invoking the navigation action,\n * covering full loads, reloads, hash changes and client-side routing.\n */\n async waitForNavigation(\n action: () => Promise<unknown>,\n options: { url?: string; timeoutMs?: number } = {},\n ): Promise<void> {\n await this.step(\"waitForNavigation\", async () => {\n const before = await this.evaluate<{ href: string; timeOrigin: number }>(\n \"({href:location.href,timeOrigin:performance.timeOrigin})\",\n );\n await action();\n await this.poll(\n \"Navigation\",\n (remaining) =>\n this.evaluate<boolean>(\n `document.readyState === 'complete' && (location.href !== ${JSON.stringify(before.href)} || performance.timeOrigin !== ${before.timeOrigin}) && ${options.url ? `location.href === ${JSON.stringify(new URL(options.url, this.origin).href)}` : \"true\"}`,\n remaining,\n ),\n options.timeoutMs ?? 10_000,\n );\n });\n }\n\n async click(selector: string, timeoutMs = 10_000): Promise<void> {\n await this.step(`click ${selector}`, async () => {\n const deadline = Date.now() + timeoutMs;\n let previous: ClickBox | undefined;\n let reason = \"layout has not settled\";\n while (Date.now() < deadline) {\n const remaining = () => Math.max(1, deadline - Date.now());\n let box: ClickBox;\n try {\n box = await this.evaluate<ClickBox>(\n `(${clickProbe.toString()})(${JSON.stringify(selector)})`,\n remaining(),\n );\n } catch (error) {\n reason = errorMessage(error);\n previous = undefined;\n await delay(25);\n continue;\n }\n reason = box.reason || \"target box is moving\";\n if (previous && boxesStable(previous, box)) {\n // Hover can move menus and overlays. Re-measure and hit-test after it.\n let hovered: ClickBox;\n try {\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n { type: \"mouseMoved\", x: box.x, y: box.y },\n remaining(),\n );\n hovered = await this.evaluate<ClickBox>(\n `(${clickProbe.toString()})(${JSON.stringify(selector)})`,\n remaining(),\n );\n } catch (error) {\n reason = errorMessage(error);\n previous = undefined;\n await delay(Math.min(25, remaining()));\n continue;\n }\n if (Date.now() < deadline && boxesStable(box, hovered)) {\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n {\n type: \"mousePressed\",\n x: hovered.x,\n y: hovered.y,\n button: \"left\",\n buttons: 1,\n clickCount: 1,\n },\n remaining(),\n );\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n {\n type: \"mouseReleased\",\n x: hovered.x,\n y: hovered.y,\n button: \"left\",\n buttons: 0,\n clickCount: 1,\n },\n remaining(),\n );\n return;\n }\n reason = hovered.reason || \"hover changed the target box\";\n previous = undefined;\n } else previous = box;\n await delay(Math.min(50, remaining()));\n }\n throw new Error(`Cannot click ${JSON.stringify(selector)} within ${timeoutMs}ms: ${reason}`);\n });\n }\n\n async fill(selector: string, value: string): Promise<void> {\n await this.step(`fill ${selector}`, async () => {\n await this.click(selector);\n // The native value setter works with controlled inputs; input/change are\n // emitted after clearing. CDP insertText then performs actual text input.\n await this.evaluate(`(() => {\n const element=document.querySelector(${JSON.stringify(selector)});\n if (element !== document.activeElement) throw new Error('Fill target lost focus');\n if ((element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) && !element.readOnly && !element.disabled) {\n const prototype=element instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;\n const setter=Object.getOwnPropertyDescriptor(prototype,'value')?.set;\n if (!setter) throw new Error('Fill target has no native value setter');\n setter.call(element,'');\n } else if (element instanceof HTMLElement && element.isContentEditable) element.textContent='';\n else throw new Error('Fill target must be an editable input, textarea or contenteditable');\n element.dispatchEvent(new Event('input',{bubbles:true}));\n })()`);\n await this.evaluate(\n `document.activeElement === document.querySelector(${JSON.stringify(selector)}) || (() => { throw new Error(\"Fill target lost focus after input\"); })()`,\n );\n if (value) await this.cdp.command(\"Input.insertText\", { text: value });\n await this.evaluate(\n \"document.activeElement?.dispatchEvent(new Event('change',{bubbles:true}))\",\n );\n });\n }\n\n /** Appends Unicode text through the browser's text-input path. */\n async type(selector: string, text: string): Promise<void> {\n await this.step(`type ${selector}`, async () => {\n await this.click(selector);\n const needsEndKey = await this.evaluate<boolean>(\n `(() => { const element=document.querySelector(${JSON.stringify(selector)}); if (element !== document.activeElement || !((element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) && !element.readOnly && !element.disabled || element instanceof HTMLElement && element.isContentEditable)) throw new Error('Type target is not focused and editable'); if(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) { if (typeof element.selectionStart !== \"number\") return true; element.setSelectionRange(element.value.length,element.value.length); } else { const range=document.createRange(); range.selectNodeContents(element); range.collapse(false); const selection=getSelection(); selection?.removeAllRanges(); selection?.addRange(range); } return false; })()`,\n );\n // Email/number inputs do not expose selectionRange; use their native\n // keyboard editing path instead of throwing on setSelectionRange.\n if (needsEndKey) {\n await this.cdp.command(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: \"End\",\n code: \"End\",\n windowsVirtualKeyCode: 35,\n });\n await this.cdp.command(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: \"End\",\n code: \"End\",\n windowsVirtualKeyCode: 35,\n });\n }\n await this.cdp.command(\"Input.insertText\", { text });\n });\n }\n\n async setViewport(viewport: Viewport): Promise<void> {\n if (\n !/^[a-z0-9_-]+$/i.test(viewport.name) ||\n !Number.isInteger(viewport.width) ||\n !Number.isInteger(viewport.height) ||\n viewport.width <= 0 ||\n viewport.height <= 0\n )\n throw new Error(\"Viewport requires a safe name and positive integer width/height\");\n await this.step(`viewport ${viewport.name} ${viewport.width}x${viewport.height}`, async () => {\n await this.cdp.command(\"Emulation.setDeviceMetricsOverride\", {\n width: viewport.width,\n height: viewport.height,\n deviceScaleFactor: 1,\n mobile: viewport.mobile ?? false,\n });\n this.viewport = { ...viewport };\n await this.evaluate(\n \"new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve(true))))\",\n );\n });\n }\n\n async assertViewport(): Promise<ViewportReport> {\n return this.step(`assertViewport ${this.viewport.name}`, async () => {\n const report = await this.evaluate<ViewportReport>(`({\n requestedWidth:${this.viewport.width},innerWidth,documentWidth:document.documentElement.scrollWidth,bodyWidth:document.body?.scrollWidth ?? 0,\n offenders:[...document.querySelectorAll('*')].map(element=>{const box=element.getBoundingClientRect();return {tag:element.tagName,id:element.id,className:element.getAttribute('class')??'',width:box.width,scrollWidth:element.scrollWidth,right:box.right,left:box.left}})\n .filter(element=>element.right>${this.viewport.width} || element.left<0 || element.width>${this.viewport.width} || element.scrollWidth>${this.viewport.width})\n .sort((a,b)=>Math.max(b.width,b.scrollWidth,b.right)-Math.max(a.width,a.scrollWidth,a.right)).slice(0,10)\n })`);\n if (\n report.innerWidth !== report.requestedWidth ||\n report.documentWidth > report.requestedWidth ||\n report.bodyWidth > report.requestedWidth\n )\n throw new Error(\n `Horizontal overflow or expanded viewport at ${this.viewport.name}: ${JSON.stringify(report)}`,\n );\n return report;\n });\n }\n\n async screenshot(name: string): Promise<string> {\n if (!/^[a-z0-9_-]+$/i.test(name))\n throw new Error(\"Screenshot names must contain only letters, digits, '_' or '-'\");\n return this.step(`screenshot ${name}`, async () => {\n const file = `${String(this.evidence.screenshots.length + 1).padStart(3, \"0\")}-${name}-${this.viewport.name}.png`;\n const { data } = await this.cdp.command(\"Page.captureScreenshot\", { format: \"png\" });\n await writeFile(join(this.outputDirectory, file), Buffer.from(data, \"base64\"));\n this.evidence.screenshots.push({ name, file, viewport: { ...this.viewport } });\n return join(this.outputDirectory, file);\n });\n }\n\n async captureViewports(\n name: string,\n viewports = this.spec.viewports ?? defaultViewports,\n ): Promise<void> {\n const failures: unknown[] = [];\n const original = this.viewport;\n for (const viewport of viewports) {\n try {\n await this.setViewport(viewport);\n try {\n await this.assertViewport();\n } finally {\n await this.screenshot(name);\n }\n } catch (error) {\n failures.push(error);\n }\n }\n try {\n await this.setViewport(original);\n } catch (error) {\n failures.push(error);\n }\n if (failures.length) throw new AggregateError(failures, failures.map(errorMessage).join(\"; \"));\n }\n\n async waitForNetworkIdle(timeoutMs = 10_000): Promise<void> {\n await this.step(\"waitForNetworkIdle\", () =>\n this.poll(\n \"Network idle\",\n async () => {\n const pending = [...this.pendingRequests.values()].filter(\n (url) => !this.spec.allowPendingRequests?.some((pattern) => matches(pattern, url)),\n );\n if (pending.length) throw new Error(`Pending requests: ${pending.join(\", \")}`);\n return Date.now() - this.lastNetworkActivity >= 300;\n },\n timeoutMs,\n ),\n );\n }\n\n assertHealthy(): void {\n for (const entry of this.evidence.console)\n if (entry.level === \"error\")\n entry.allowed =\n this.spec.allowConsoleErrors?.some((pattern) => matches(pattern, entry.text)) ?? false;\n for (const entry of this.evidence.networkFailures)\n entry.allowed =\n this.spec.allowNetworkFailures?.some(\n (rule) =>\n matches(rule.url, entry.url) &&\n (rule.status === undefined || rule.status === entry.status) &&\n (rule.errorText === undefined || matches(rule.errorText, entry.errorText ?? \"\")),\n ) ?? false;\n const consoleErrors = this.evidence.console.filter(\n (entry) => entry.level === \"error\" && !entry.allowed,\n );\n const networkErrors = this.evidence.networkFailures.filter((entry) => !entry.allowed);\n if (consoleErrors.length || networkErrors.length)\n throw new Error(\n `Browser health failed: ${consoleErrors.length} console errors, ${networkErrors.length} failed HTTP/network requests\\n${JSON.stringify({ console: consoleErrors, network: networkErrors })}`,\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page.mjs","names":["delay"],"sources":["../../../../../../../../packages/core/src/e2e/page.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { CdpConnection, CdpTimeoutError } from \"./cdp\";\nimport {\n type AccessibilityOptions,\n type AccessibilityEvidence,\n defaultViewports,\n errorMessage,\n matches,\n type E2EEvidence,\n type E2ESpec,\n type PayloadEvidence,\n type Viewport,\n} from \"./types\";\n\ntype WaitOptions = { selector?: string; text?: string; url?: string; timeoutMs?: number };\ntype ClickBox = { x: number; y: number; width: number; height: number; reason: string };\ntype ViewportReport = {\n requestedWidth: number;\n innerWidth: number;\n documentWidth: number;\n bodyWidth: number;\n offenders: {\n tag: string;\n id: string;\n className: string;\n width: number;\n scrollWidth: number;\n right: number;\n left: number;\n }[];\n};\n\n/** Runs inside the browser. Two paints plus cross-probe box comparison detect\n * scroll/animation/layout races. The hit test also accepts target descendants.\n */\nfunction clickProbe(selector: string): Promise<ClickBox> {\n return (async () => {\n const empty = (reason: string): ClickBox => ({ x: 0, y: 0, width: 0, height: 0, reason });\n const element = document.querySelector(selector);\n if (!(element instanceof HTMLElement || element instanceof SVGElement))\n return empty(\"target is missing or is not an HTML/SVG element\");\n element.scrollIntoView({ block: \"center\", inline: \"center\", behavior: \"instant\" });\n await new Promise<void>((resolve) =>\n requestAnimationFrame(() => requestAnimationFrame(() => resolve())),\n );\n if (!element.isConnected || document.querySelector(selector) !== element)\n return empty(\"target was replaced during layout\");\n const box = element.getBoundingClientRect();\n const x = box.x + box.width / 2;\n const y = box.y + box.height / 2;\n const style = getComputedStyle(element);\n let reason = \"\";\n if (\n box.width <= 0 ||\n box.height <= 0 ||\n style.visibility !== \"visible\" ||\n style.display === \"none\" ||\n Number(style.opacity) === 0\n )\n reason = \"target is hidden or has an empty box\";\n else if (element.matches(\":disabled\") || element.closest('[inert], [aria-disabled=\"true\"]'))\n reason = \"target is disabled or inert\";\n else if (x < 0 || y < 0 || x >= innerWidth || y >= innerHeight)\n reason = \"target center is outside the viewport\";\n else {\n const top = document.elementFromPoint(x, y);\n if (!top || !element.contains(top))\n reason = `target is covered by ${top?.tagName ?? \"no element\"}${top?.id ? \"#\" + top.id : \"\"}`;\n }\n return { x, y, width: box.width, height: box.height, reason };\n })();\n}\n\nexport function boxesStable(first: ClickBox, second: ClickBox): boolean {\n return (\n !first.reason &&\n !second.reason &&\n ([\"x\", \"y\", \"width\", \"height\"] as const).every(\n (key) => Math.abs(first[key] - second[key]) < 0.25,\n )\n );\n}\n\n/** Reason to report after a failed probe in a retry loop. A CDP timeout comes from\n * the deadline shrinking under the probe, not from the target, so it must not\n * replace a reason already observed on the target. Any other error is reported.\n */\nfunction probeReason(error: unknown, observed: string | undefined): string {\n if (error instanceof CdpTimeoutError && observed !== undefined) return observed;\n return errorMessage(error);\n}\n\nexport class BrowserPage {\n private viewport: Viewport = { ...defaultViewports[0] };\n private loads = 0;\n private pendingRequests = new Map<string, string>();\n private requestUrls = new Map<string, string>();\n private lastNetworkActivity = Date.now();\n\n constructor(\n private cdp: CdpConnection,\n private origin: string,\n private outputDirectory: string,\n private evidence: E2EEvidence,\n private spec: E2ESpec,\n ) {\n cdp.on(\"Page.loadEventFired\", () => {\n this.loads++;\n });\n cdp.on(\"Runtime.consoleAPICalled\", ({ type, args }) =>\n evidence.console.push({\n at: new Date().toISOString(),\n level: type === \"assert\" ? \"error\" : type,\n text: args.map((arg) => String(arg.value ?? arg.description ?? \"\")).join(\" \"),\n }),\n );\n cdp.on(\"Runtime.exceptionThrown\", ({ exceptionDetails }) =>\n evidence.console.push({\n at: new Date().toISOString(),\n level: \"error\",\n text: exceptionDetails.exception?.description ?? exceptionDetails.text,\n }),\n );\n cdp.on(\"Log.entryAdded\", ({ entry }) =>\n evidence.console.push({ at: new Date().toISOString(), level: entry.level, text: entry.text }),\n );\n cdp.on(\"Network.requestWillBeSent\", ({ requestId, request }) => {\n this.pendingRequests.set(requestId, request.url);\n this.requestUrls.set(requestId, request.url);\n this.lastNetworkActivity = Date.now();\n });\n cdp.on(\"Network.responseReceived\", ({ requestId, response }) => {\n this.requestUrls.set(requestId, response.url);\n if (response.status >= 400)\n evidence.networkFailures.push({\n at: new Date().toISOString(),\n url: response.url,\n status: response.status,\n });\n });\n cdp.on(\"Network.loadingFailed\", ({ requestId, errorText, canceled }) => {\n evidence.networkFailures.push({\n at: new Date().toISOString(),\n url: this.requestUrls.get(requestId) ?? `unknown request ${requestId}`,\n errorText,\n canceled,\n });\n this.completeRequest(requestId);\n });\n cdp.on(\"Network.loadingFinished\", ({ requestId }) => this.completeRequest(requestId));\n }\n\n private completeRequest(requestId: string): void {\n this.pendingRequests.delete(requestId);\n this.requestUrls.delete(requestId);\n this.lastNetworkActivity = Date.now();\n }\n\n async initialize(): Promise<void> {\n await this.cdp.command(\"Page.enable\", {});\n await this.cdp.command(\"Runtime.enable\", {});\n await this.cdp.command(\"Network.enable\", {});\n await this.cdp.command(\"Log.enable\", {});\n await this.setViewport(this.spec.viewports?.[0] ?? defaultViewports[0]);\n }\n\n async step<T>(name: string, action: () => Promise<T>): Promise<T> {\n const start = performance.now();\n const entry = {\n name,\n startedAt: new Date().toISOString(),\n durationMs: 0,\n status: \"passed\" as \"passed\" | \"failed\",\n error: undefined as string | undefined,\n };\n this.evidence.steps.push(entry);\n try {\n return await action();\n } catch (error) {\n entry.status = \"failed\";\n entry.error = errorMessage(error);\n throw error;\n } finally {\n entry.durationMs = Math.round(performance.now() - start);\n }\n }\n\n async evaluate<T = unknown>(expression: string, timeoutMs = 10_000): Promise<T> {\n const response = await this.cdp.command(\n \"Runtime.evaluate\",\n { expression, returnByValue: true, awaitPromise: true, silent: true },\n timeoutMs,\n );\n if (response.exceptionDetails)\n throw new Error(\n `Browser evaluation failed: ${response.exceptionDetails.exception?.description ?? response.exceptionDetails.text}`,\n );\n return response.result.value as T;\n }\n\n private async poll(\n description: string,\n check: (remainingMs: number) => Promise<boolean>,\n timeoutMs: number,\n ): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n let reason: string | undefined;\n while (Date.now() < deadline) {\n try {\n if (await check(Math.max(1, deadline - Date.now()))) return;\n } catch (error) {\n reason = probeReason(error, reason);\n }\n await delay(Math.min(50, Math.max(0, deadline - Date.now())));\n }\n throw new Error(\n `${description} timed out after ${timeoutMs}ms: ${reason ?? \"condition stayed false\"}`,\n );\n }\n\n async goto(path: string, timeoutMs = 10_000): Promise<void> {\n await this.step(`goto ${path}`, async () => {\n const url = new URL(path, this.origin).href;\n const before = this.loads;\n const navigation = await this.cdp.command(\"Page.navigate\", { url }, timeoutMs);\n if (navigation.errorText) throw new Error(`Navigation failed: ${navigation.errorText}`);\n await this.poll(\n `Navigation to ${url}`,\n (remaining) =>\n this.evaluate<boolean>(\n `document.readyState === 'complete' && ${navigation.loaderId ? this.loads > before : \"true\"}`,\n remaining,\n ),\n timeoutMs,\n );\n });\n }\n\n async waitFor(options: WaitOptions): Promise<void> {\n if (options.selector === undefined && options.text === undefined && options.url === undefined)\n throw new Error(\"waitFor requires a selector, text, or URL\");\n await this.step(`waitFor ${JSON.stringify(options)}`, () =>\n this.poll(\n \"waitFor \" + JSON.stringify(options),\n (remaining) =>\n this.evaluate<boolean>(\n `(() => {\n const options = ${JSON.stringify(options)};\n const element = options.selector ? document.querySelector(options.selector) : document.body;\n if (!element) return false;\n if (options.selector) { const box=element.getBoundingClientRect(); const css=getComputedStyle(element); if (!box.width || !box.height || css.visibility !== 'visible') return false; }\n return (options.text === undefined || (element.innerText ?? element.textContent ?? '').includes(options.text)) &&\n (options.url === undefined || location.href === new URL(options.url, ${JSON.stringify(this.origin)}).href);\n })()`,\n remaining,\n ),\n options.timeoutMs ?? 10_000,\n ),\n );\n }\n\n /** Arms the document/URL comparison BEFORE invoking the navigation action,\n * covering full loads, reloads, hash changes and client-side routing.\n */\n async waitForNavigation(\n action: () => Promise<unknown>,\n options: { url?: string; timeoutMs?: number } = {},\n ): Promise<void> {\n await this.step(\"waitForNavigation\", async () => {\n const before = await this.evaluate<{ href: string; timeOrigin: number }>(\n \"({href:location.href,timeOrigin:performance.timeOrigin})\",\n );\n await action();\n await this.poll(\n \"Navigation\",\n (remaining) =>\n this.evaluate<boolean>(\n `document.readyState === 'complete' && (location.href !== ${JSON.stringify(before.href)} || performance.timeOrigin !== ${before.timeOrigin}) && ${options.url ? `location.href === ${JSON.stringify(new URL(options.url, this.origin).href)}` : \"true\"}`,\n remaining,\n ),\n options.timeoutMs ?? 10_000,\n );\n });\n }\n\n async click(selector: string, timeoutMs = 10_000): Promise<void> {\n await this.step(`click ${selector}`, async () => {\n const deadline = Date.now() + timeoutMs;\n let previous: ClickBox | undefined;\n let reason: string | undefined;\n while (Date.now() < deadline) {\n const remaining = () => Math.max(1, deadline - Date.now());\n let box: ClickBox;\n try {\n box = await this.evaluate<ClickBox>(\n `(${clickProbe.toString()})(${JSON.stringify(selector)})`,\n remaining(),\n );\n } catch (error) {\n reason = probeReason(error, reason);\n previous = undefined;\n await delay(25);\n continue;\n }\n reason = box.reason || \"target box is moving\";\n if (previous && boxesStable(previous, box)) {\n // Hover can move menus and overlays. Re-measure and hit-test after it.\n let hovered: ClickBox;\n try {\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n { type: \"mouseMoved\", x: box.x, y: box.y },\n remaining(),\n );\n hovered = await this.evaluate<ClickBox>(\n `(${clickProbe.toString()})(${JSON.stringify(selector)})`,\n remaining(),\n );\n } catch (error) {\n reason = probeReason(error, reason);\n previous = undefined;\n await delay(Math.min(25, remaining()));\n continue;\n }\n if (Date.now() < deadline && boxesStable(box, hovered)) {\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n {\n type: \"mousePressed\",\n x: hovered.x,\n y: hovered.y,\n button: \"left\",\n buttons: 1,\n clickCount: 1,\n },\n remaining(),\n );\n await this.cdp.command(\n \"Input.dispatchMouseEvent\",\n {\n type: \"mouseReleased\",\n x: hovered.x,\n y: hovered.y,\n button: \"left\",\n buttons: 0,\n clickCount: 1,\n },\n remaining(),\n );\n return;\n }\n reason = hovered.reason || \"hover changed the target box\";\n previous = undefined;\n } else previous = box;\n await delay(Math.min(50, remaining()));\n }\n throw new Error(\n `Cannot click ${JSON.stringify(selector)} within ${timeoutMs}ms: ${reason ?? \"layout has not settled\"}`,\n );\n });\n }\n\n async fill(selector: string, value: string): Promise<void> {\n await this.step(`fill ${selector}`, async () => {\n await this.click(selector);\n // The native value setter works with controlled inputs; input/change are\n // emitted after clearing. CDP insertText then performs actual text input.\n await this.evaluate(`(() => {\n const element=document.querySelector(${JSON.stringify(selector)});\n if (element !== document.activeElement) throw new Error('Fill target lost focus');\n if ((element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) && !element.readOnly && !element.disabled) {\n const prototype=element instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype;\n const setter=Object.getOwnPropertyDescriptor(prototype,'value')?.set;\n if (!setter) throw new Error('Fill target has no native value setter');\n setter.call(element,'');\n } else if (element instanceof HTMLElement && element.isContentEditable) element.textContent='';\n else throw new Error('Fill target must be an editable input, textarea or contenteditable');\n element.dispatchEvent(new Event('input',{bubbles:true}));\n })()`);\n await this.evaluate(\n `document.activeElement === document.querySelector(${JSON.stringify(selector)}) || (() => { throw new Error(\"Fill target lost focus after input\"); })()`,\n );\n if (value) await this.cdp.command(\"Input.insertText\", { text: value });\n await this.evaluate(\n \"document.activeElement?.dispatchEvent(new Event('change',{bubbles:true}))\",\n );\n });\n }\n\n /** Appends Unicode text through the browser's text-input path. */\n async type(selector: string, text: string): Promise<void> {\n await this.step(`type ${selector}`, async () => {\n await this.click(selector);\n const needsEndKey = await this.evaluate<boolean>(\n `(() => { const element=document.querySelector(${JSON.stringify(selector)}); if (element !== document.activeElement || !((element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) && !element.readOnly && !element.disabled || element instanceof HTMLElement && element.isContentEditable)) throw new Error('Type target is not focused and editable'); if(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement) { if (typeof element.selectionStart !== \"number\") return true; element.setSelectionRange(element.value.length,element.value.length); } else { const range=document.createRange(); range.selectNodeContents(element); range.collapse(false); const selection=getSelection(); selection?.removeAllRanges(); selection?.addRange(range); } return false; })()`,\n );\n // Email/number inputs do not expose selectionRange; use their native\n // keyboard editing path instead of throwing on setSelectionRange.\n if (needsEndKey) {\n await this.cdp.command(\"Input.dispatchKeyEvent\", {\n type: \"keyDown\",\n key: \"End\",\n code: \"End\",\n windowsVirtualKeyCode: 35,\n });\n await this.cdp.command(\"Input.dispatchKeyEvent\", {\n type: \"keyUp\",\n key: \"End\",\n code: \"End\",\n windowsVirtualKeyCode: 35,\n });\n }\n await this.cdp.command(\"Input.insertText\", { text });\n });\n }\n\n async setViewport(viewport: Viewport): Promise<void> {\n if (\n !/^[a-z0-9_-]+$/i.test(viewport.name) ||\n !Number.isInteger(viewport.width) ||\n !Number.isInteger(viewport.height) ||\n viewport.width <= 0 ||\n viewport.height <= 0\n )\n throw new Error(\"Viewport requires a safe name and positive integer width/height\");\n await this.step(`viewport ${viewport.name} ${viewport.width}x${viewport.height}`, async () => {\n await this.cdp.command(\"Emulation.setDeviceMetricsOverride\", {\n width: viewport.width,\n height: viewport.height,\n deviceScaleFactor: 1,\n mobile: viewport.mobile ?? false,\n });\n this.viewport = { ...viewport };\n await this.evaluate(\n \"new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve(true))))\",\n );\n });\n }\n\n /** Inject the installed axe distribution through Runtime.evaluate on every scan.\n * API: https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#api-name-axerun\n * No CDN, DOM script element, or application dependency is required.\n */\n /**\n * Measures the inline payload of the document the page currently shows and\n * records it under `evidence.payloads`. Measure after a full load: a client\n * navigation replaces the data it reads. Pass `limit` to mark an overage.\n */\n async measurePayload(options: { limit?: number } = {}): Promise<PayloadEvidence> {\n return this.step(`measurePayload ${this.viewport.name}`, async () => {\n const measured = await this.evaluate<Pick<PayloadEvidence, \"url\" | \"data\" | \"routes\">>(\n `(() => {\n const bytes = (id) => {\n const node = document.getElementById(id);\n return node === null ? 0 : new TextEncoder().encode(node.textContent || \"\").length;\n };\n return { url: location.href, data: bytes(\"__WARLOCK_DATA__\"), routes: bytes(\"__WARLOCK_NAMED_API_ROUTES__\") };\n })()`,\n 10_000,\n );\n const total = measured.data + measured.routes;\n const report: PayloadEvidence = {\n at: new Date().toISOString(),\n viewport: { ...this.viewport },\n ...measured,\n total,\n ...(options.limit === undefined\n ? {}\n : { limit: options.limit, overBudget: total > options.limit }),\n };\n (this.evidence.payloads ??= []).push(report);\n return report;\n });\n }\n\n async assertAccessible(options: AccessibilityOptions = {}): Promise<AccessibilityEvidence> {\n return this.step(`assertAccessible ${this.viewport.name}`, async () => {\n const source = await readFile(\n createRequire(import.meta.url).resolve(\"axe-core/axe.min.js\"),\n \"utf8\",\n );\n const timeout = options.timeoutMs ?? 30_000;\n await this.evaluate(source, timeout);\n const result = await this.evaluate<\n Pick<AccessibilityEvidence, \"url\" | \"violations\" | \"incomplete\">\n >(\n `(async () => {\n const selector = ${JSON.stringify(options.selector ?? null)};\n const context = selector === null ? document : document.querySelector(selector);\n if (!context) throw new Error('Accessibility scan selector did not match: ' + selector);\n const results = await axe.run(context, ${JSON.stringify(options.tags ? { runOnly: { type: \"tag\", values: options.tags } } : {})});\n const summarize = entries => entries.map(({id, impact, help, helpUrl, nodes}) => ({\n id, impact, help, helpUrl, nodes: nodes.map(({target, failureSummary}) => ({target, failureSummary}))\n }));\n return {url: location.href, violations: summarize(results.violations), incomplete: summarize(results.incomplete)};\n })()`,\n timeout,\n );\n for (const violation of result.violations) {\n for (const node of violation.nodes) {\n node.allowed =\n options.allow?.some(\n (rule) =>\n rule.id === violation.id &&\n (rule.selector === undefined ||\n (node.target.length === 1 && node.target[0] === rule.selector)),\n ) ?? false;\n }\n }\n const report = { at: new Date().toISOString(), viewport: { ...this.viewport }, ...result };\n (this.evidence.accessibility ??= []).push(report);\n const failures = report.violations.flatMap((violation) =>\n violation.nodes\n .filter((node) => !node.allowed)\n .map((node) => `${violation.id}: ${JSON.stringify(node.target)} — ${violation.help}`),\n );\n if (failures.length) throw new Error(`Accessibility violations:\\n${failures.join(\"\\n\")}`);\n return report;\n });\n }\n\n async assertViewport(): Promise<ViewportReport> {\n return this.step(`assertViewport ${this.viewport.name}`, async () => {\n const report = await this.evaluate<ViewportReport>(`({\n requestedWidth:${this.viewport.width},innerWidth,documentWidth:document.documentElement.scrollWidth,bodyWidth:document.body?.scrollWidth ?? 0,\n offenders:[...document.querySelectorAll('*')].map(element=>{const box=element.getBoundingClientRect();return {tag:element.tagName,id:element.id,className:element.getAttribute('class')??'',width:box.width,scrollWidth:element.scrollWidth,right:box.right,left:box.left}})\n .filter(element=>element.right>${this.viewport.width} || element.left<0 || element.width>${this.viewport.width} || element.scrollWidth>${this.viewport.width})\n .sort((a,b)=>Math.max(b.width,b.scrollWidth,b.right)-Math.max(a.width,a.scrollWidth,a.right)).slice(0,10)\n })`);\n if (\n report.innerWidth !== report.requestedWidth ||\n report.documentWidth > report.requestedWidth ||\n report.bodyWidth > report.requestedWidth\n )\n throw new Error(\n `Horizontal overflow or expanded viewport at ${this.viewport.name}: ${JSON.stringify(report)}`,\n );\n return report;\n });\n }\n\n async screenshot(name: string): Promise<string> {\n if (!/^[a-z0-9_-]+$/i.test(name))\n throw new Error(\"Screenshot names must contain only letters, digits, '_' or '-'\");\n return this.step(`screenshot ${name}`, async () => {\n const file = `${String(this.evidence.screenshots.length + 1).padStart(3, \"0\")}-${name}-${this.viewport.name}.png`;\n const { data } = await this.cdp.command(\"Page.captureScreenshot\", { format: \"png\" });\n await writeFile(join(this.outputDirectory, file), Buffer.from(data, \"base64\"));\n this.evidence.screenshots.push({ name, file, viewport: { ...this.viewport } });\n return join(this.outputDirectory, file);\n });\n }\n\n async captureViewports(\n name: string,\n viewports = this.spec.viewports ?? defaultViewports,\n ): Promise<void> {\n const failures: unknown[] = [];\n const original = this.viewport;\n for (const viewport of viewports) {\n try {\n await this.setViewport(viewport);\n try {\n await this.assertViewport();\n } finally {\n await this.screenshot(name);\n }\n } catch (error) {\n failures.push(error);\n }\n }\n try {\n await this.setViewport(original);\n } catch (error) {\n failures.push(error);\n }\n if (failures.length) throw new AggregateError(failures, failures.map(errorMessage).join(\"; \"));\n }\n\n async waitForNetworkIdle(timeoutMs = 10_000): Promise<void> {\n await this.step(\"waitForNetworkIdle\", () =>\n this.poll(\n \"Network idle\",\n async () => {\n const pending = [...this.pendingRequests.values()].filter(\n (url) => !this.spec.allowPendingRequests?.some((pattern) => matches(pattern, url)),\n );\n if (pending.length) throw new Error(`Pending requests: ${pending.join(\", \")}`);\n return Date.now() - this.lastNetworkActivity >= 300;\n },\n timeoutMs,\n ),\n );\n }\n\n assertHealthy(): void {\n for (const entry of this.evidence.console)\n if (entry.level === \"error\")\n entry.allowed =\n this.spec.allowConsoleErrors?.some((pattern) => matches(pattern, entry.text)) ?? false;\n for (const entry of this.evidence.networkFailures)\n entry.allowed =\n this.spec.allowNetworkFailures?.some(\n (rule) =>\n matches(rule.url, entry.url) &&\n (rule.status === undefined || rule.status === entry.status) &&\n (rule.errorText === undefined || matches(rule.errorText, entry.errorText ?? \"\")),\n ) ?? false;\n const consoleErrors = this.evidence.console.filter(\n (entry) => entry.level === \"error\" && !entry.allowed,\n );\n const networkErrors = this.evidence.networkFailures.filter((entry) => !entry.allowed);\n if (consoleErrors.length || networkErrors.length)\n throw new Error(\n `Browser health failed: ${consoleErrors.length} console errors, ${networkErrors.length} failed HTTP/network requests\\n${JSON.stringify({ console: consoleErrors, network: networkErrors })}`,\n );\n }\n}\n"],"mappings":"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"}
|
package/esm/e2e/types.d.mts
CHANGED
|
@@ -78,6 +78,53 @@ export type ProcessEvidence = {
|
|
|
78
78
|
ownerFile: string;
|
|
79
79
|
stopped?: boolean;
|
|
80
80
|
};
|
|
81
|
+
export type AccessibilityOptions = {
|
|
82
|
+
/** CSS selector limiting the scan; defaults to the document. */
|
|
83
|
+
selector?: string;
|
|
84
|
+
/** axe rule tags, for example wcag2a and wcag2aa. Defaults to all enabled rules. */
|
|
85
|
+
tags?: string[];
|
|
86
|
+
/** Exact rule ID, optionally limited to an exact reported selector. */
|
|
87
|
+
allow?: {
|
|
88
|
+
id: string;
|
|
89
|
+
selector?: string;
|
|
90
|
+
}[];
|
|
91
|
+
timeoutMs?: number;
|
|
92
|
+
};
|
|
93
|
+
export type AccessibilityViolation = {
|
|
94
|
+
id: string;
|
|
95
|
+
impact: string | null;
|
|
96
|
+
help: string;
|
|
97
|
+
helpUrl: string;
|
|
98
|
+
nodes: {
|
|
99
|
+
target: (string | string[])[];
|
|
100
|
+
failureSummary?: string;
|
|
101
|
+
allowed?: boolean;
|
|
102
|
+
}[];
|
|
103
|
+
};
|
|
104
|
+
export type AccessibilityEvidence = {
|
|
105
|
+
at: string;
|
|
106
|
+
url: string;
|
|
107
|
+
viewport: Viewport;
|
|
108
|
+
violations: AccessibilityViolation[];
|
|
109
|
+
incomplete: AccessibilityViolation[];
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The inline payload of one loaded document, in bytes as the HTML carries it:
|
|
113
|
+
* the hydration data script and the browser route table. `limit` and
|
|
114
|
+
* `overBudget` are present when the caller passed a limit to `measurePayload`.
|
|
115
|
+
*/
|
|
116
|
+
export type PayloadEvidence = {
|
|
117
|
+
at: string;
|
|
118
|
+
url: string;
|
|
119
|
+
viewport: Viewport;
|
|
120
|
+
/** The `__WARLOCK_DATA__` script: page, layout and app data, translations, session. */
|
|
121
|
+
data: number;
|
|
122
|
+
/** The `__WARLOCK_NAMED_API_ROUTES__` script: the page's browser route table. */
|
|
123
|
+
routes: number;
|
|
124
|
+
total: number;
|
|
125
|
+
limit?: number;
|
|
126
|
+
overBudget?: boolean;
|
|
127
|
+
};
|
|
81
128
|
export type E2EEvidence = {
|
|
82
129
|
schemaVersion: 1;
|
|
83
130
|
startedAt: string;
|
|
@@ -92,6 +139,8 @@ export type E2EEvidence = {
|
|
|
92
139
|
file: string;
|
|
93
140
|
viewport: Viewport;
|
|
94
141
|
}[];
|
|
142
|
+
accessibility?: AccessibilityEvidence[];
|
|
143
|
+
payloads?: PayloadEvidence[];
|
|
95
144
|
processes: ProcessEvidence[];
|
|
96
145
|
errors: string[];
|
|
97
146
|
};
|
package/esm/e2e/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/types.ts"],"mappings":";;YAEY,oBAAoB,uBAAuB;YAC3C,mBAAmB;YACnB;EAAa;EAAc;EAAe;EAAgB;;qBACzD,2BAA2B;YAK5B;;EAEV;EACA;;EAEA;EACA;;EAEA;;YAEU;EACV;;EAEA,QAAQ,YAAY,SAAS,mBAAmB;;EAEhD,QAAQ,YAAY,SAAS,mBAAmB;;EAEhD,MAAM,2BAA2B,SAAS,mBAAmB;;EAE7D;;EAEA,WAAW,SAAS,0BAA0B;;EAE9C;EACA;;EAEA;EACA;;YAEU;EACV;;EAEA;;;;;;EAMA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA;;YAEU;EAAiB;EAAY;EAAe;EAAc;;YAC1D;EACV;EACA;EACA;EACA;EACA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,SAAS;EACT,iBAAiB;EACjB;IAAe;IAAc;IAAc,UAAU;;EACrD,WAAW;EACX;;YAEU;EAAqB,KAAK;EAAS;EAAiB,YAAY;;YAChE,aAAa;EACvB,MAAM;EACN,UAAU;EACV;EACA,QAAQ;EACR,OAAO,GAAG,cAAc,cAAc,QAAQ,OAAO,QAAQ;;YAEnD;EACV,KAAK;EACL,UAAU;EACV,qBAAqB;EACrB,qBAAqB;EACrB,uBAAuB;;EAEvB,uBAAuB;EACvB;EACA,MAAM,SAAS,eAAe;;YAEpB;EACV;EACA;EACA;EACA,SAAS;;wBAGK,UAAU,MAAM,UAAU"}
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../../packages/core/src/e2e/types.ts"],"mappings":";;YAEY,oBAAoB,uBAAuB;YAC3C,mBAAmB;YACnB;EAAa;EAAc;EAAe;EAAgB;;qBACzD,2BAA2B;YAK5B;;EAEV;EACA;;EAEA;EACA;;EAEA;;YAEU;EACV;;EAEA,QAAQ,YAAY,SAAS,mBAAmB;;EAEhD,QAAQ,YAAY,SAAS,mBAAmB;;EAEhD,MAAM,2BAA2B,SAAS,mBAAmB;;EAE7D;;EAEA,WAAW,SAAS,0BAA0B;;EAE9C;EACA;;EAEA;EACA;;YAEU;EACV;;EAEA;;;;;;EAMA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA;;YAEU;EAAiB;EAAY;EAAe;EAAc;;YAC1D;EACV;EACA;EACA;EACA;EACA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA;;YAEU;;EAEV;;EAEA;;EAEA;IAAU;IAAY;;EACtB;;YAEU;EACV;EACA;EACA;EACA;EACA;IAAS;IAA+B;IAAyB;;;YAEvD;EACV;EACA;EACA,UAAU;EACV,YAAY;EACZ,YAAY;;;;;;;YAOF;EACV;EACA;EACA,UAAU;;EAEV;;EAEA;EACA;EACA;EACA;;YAEU;EACV;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,SAAS;EACT,iBAAiB;EACjB;IAAe;IAAc;IAAc,UAAU;;EACrD,gBAAgB;EAChB,WAAW;EACX,WAAW;EACX;;YAEU;EAAqB,KAAK;EAAS;EAAiB,YAAY;;YAChE,aAAa;EACvB,MAAM;EACN,UAAU;EACV;EACA,QAAQ;EACR,OAAO,GAAG,cAAc,cAAc,QAAQ,OAAO,QAAQ;;YAEnD;EACV,KAAK;EACL,UAAU;EACV,qBAAqB;EACrB,qBAAqB;EACrB,uBAAuB;;EAEvB,uBAAuB;EACvB;EACA,MAAM,SAAS,eAAe;;YAEpB;EACV;EACA;EACA;EACA,SAAS;;wBAGK,UAAU,MAAM,UAAU"}
|
package/esm/e2e/types.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../../packages/core/src/e2e/types.ts"],"sourcesContent":["import type { BrowserPage } from \"./page\";\n\nexport type Command = readonly [executable: string, ...args: string[]];\nexport type Pattern = string | RegExp;\nexport type Viewport = { name: string; width: number; height: number; mobile?: boolean };\nexport const defaultViewports: readonly Viewport[] = [\n { name: \"desktop\", width: 1280, height: 800 },\n { name: \"phone\", width: 390, height: 844, mobile: true },\n];\n\nexport type FixtureContext = {\n /** Disposable copy of the app. All commands run here. */\n directory: string;\n dataRoot: string;\n /** OS-selected ephemeral port, reserved until immediately before app startup. */\n port: number;\n url: string;\n /** Write an actual port here when the app binds port 0 itself. */\n portFile: string;\n};\nexport type AppFixtureOptions = {\n directory?: string;\n /** Optional build, executed inside the disposable copy before start. */\n build?: Command | ((fixture: FixtureContext) => Command);\n /** Defaults to [process.execPath, \"dist/app.js\"]. Must stay in the foreground. */\n start?: Command | ((fixture: FixtureContext) => Command);\n /** Only these app variables are added to the isolated environment. */\n env?: Record<string, string> | ((fixture: FixtureContext) => Record<string, string>);\n /** Directories prepended to PATH, for fixture-owned fake engines/binaries. */\n path?: string[];\n /** Prepare data/fake binaries in the disposable fixture before build/start. */\n prepare?: (fixture: FixtureContext) => void | Promise<void>;\n /** Additional relative directories to omit when copying app files. */\n exclude?: string[];\n readyPath?: string;\n /** If true, await portFile instead of using the preallocated port. */\n portFromFile?: boolean;\n startupTimeoutMs?: number;\n};\nexport type BrowserOptions = {\n binary?: string;\n /** Additional Chrome flags, e.g. --no-sandbox in a controlled root container. */\n args?: string[];\n /**\n * How long to wait for the browser to report its DevTools endpoint.\n * A first launch with a fresh profile on a busy machine can be slow.\n * @default 30_000\n */\n startupTimeoutMs?: number;\n};\nexport type NetworkFailure = {\n at: string;\n url: string;\n status?: number;\n errorText?: string;\n canceled?: boolean;\n allowed?: boolean;\n};\nexport type ConsoleEntry = { at: string; level: string; text: string; allowed?: boolean };\nexport type StepEvidence = {\n name: string;\n startedAt: string;\n durationMs: number;\n status: \"passed\" | \"failed\";\n error?: string;\n};\nexport type ProcessEvidence = {\n role: string;\n pid: number;\n childPid?: number;\n command: string[];\n ownerFile: string;\n stopped?: boolean;\n};\nexport type E2EEvidence = {\n schemaVersion: 1;\n startedAt: string;\n durationMs: number;\n status: \"running\" | \"passed\" | \"failed\";\n url?: string;\n steps: StepEvidence[];\n console: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n screenshots: { name: string; file: string; viewport: Viewport }[];\n processes: ProcessEvidence[];\n errors: string[];\n};\nexport type NetworkAllowance = { url: Pattern; status?: number; errorText?: Pattern };\nexport type E2EContext = FixtureContext & {\n page: BrowserPage;\n evidence: E2EEvidence;\n outputDirectory: string;\n signal: AbortSignal;\n step: <T>(name: string, action: () => Promise<T>) => Promise<T>;\n};\nexport type E2ESpec = {\n app: AppFixtureOptions;\n browser?: BrowserOptions;\n viewports?: readonly Viewport[];\n allowConsoleErrors?: Pattern[];\n allowNetworkFailures?: NetworkAllowance[];\n /** Long-lived HTTP requests excluded from the final network-idle barrier. */\n allowPendingRequests?: Pattern[];\n timeoutMs?: number;\n run: (context: E2EContext) => Promise<void>;\n};\nexport type RunE2EOptions = {\n browser?: string;\n path?: string[];\n outputDirectory?: string;\n signal?: AbortSignal;\n};\n\nexport function defineE2E(spec: E2ESpec): E2ESpec {\n return spec;\n}\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\nexport function matches(pattern: Pattern, value: string): boolean {\n // A fresh regexp avoids stateful /g and /y allowlists changing their verdict.\n return typeof pattern === \"string\"\n ? value.includes(pattern)\n : new RegExp(pattern.source, pattern.flags).test(value);\n}\n"],"mappings":";AAKA,MAAa,mBAAwC,CACnD;CAAE,MAAM;CAAW,OAAO;CAAM,QAAQ;AAAI,GAC5C;CAAE,MAAM;CAAS,OAAO;CAAK,QAAQ;CAAK,QAAQ;AAAK,CACzD;
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../../packages/core/src/e2e/types.ts"],"sourcesContent":["import type { BrowserPage } from \"./page\";\n\nexport type Command = readonly [executable: string, ...args: string[]];\nexport type Pattern = string | RegExp;\nexport type Viewport = { name: string; width: number; height: number; mobile?: boolean };\nexport const defaultViewports: readonly Viewport[] = [\n { name: \"desktop\", width: 1280, height: 800 },\n { name: \"phone\", width: 390, height: 844, mobile: true },\n];\n\nexport type FixtureContext = {\n /** Disposable copy of the app. All commands run here. */\n directory: string;\n dataRoot: string;\n /** OS-selected ephemeral port, reserved until immediately before app startup. */\n port: number;\n url: string;\n /** Write an actual port here when the app binds port 0 itself. */\n portFile: string;\n};\nexport type AppFixtureOptions = {\n directory?: string;\n /** Optional build, executed inside the disposable copy before start. */\n build?: Command | ((fixture: FixtureContext) => Command);\n /** Defaults to [process.execPath, \"dist/app.js\"]. Must stay in the foreground. */\n start?: Command | ((fixture: FixtureContext) => Command);\n /** Only these app variables are added to the isolated environment. */\n env?: Record<string, string> | ((fixture: FixtureContext) => Record<string, string>);\n /** Directories prepended to PATH, for fixture-owned fake engines/binaries. */\n path?: string[];\n /** Prepare data/fake binaries in the disposable fixture before build/start. */\n prepare?: (fixture: FixtureContext) => void | Promise<void>;\n /** Additional relative directories to omit when copying app files. */\n exclude?: string[];\n readyPath?: string;\n /** If true, await portFile instead of using the preallocated port. */\n portFromFile?: boolean;\n startupTimeoutMs?: number;\n};\nexport type BrowserOptions = {\n binary?: string;\n /** Additional Chrome flags, e.g. --no-sandbox in a controlled root container. */\n args?: string[];\n /**\n * How long to wait for the browser to report its DevTools endpoint.\n * A first launch with a fresh profile on a busy machine can be slow.\n * @default 30_000\n */\n startupTimeoutMs?: number;\n};\nexport type NetworkFailure = {\n at: string;\n url: string;\n status?: number;\n errorText?: string;\n canceled?: boolean;\n allowed?: boolean;\n};\nexport type ConsoleEntry = { at: string; level: string; text: string; allowed?: boolean };\nexport type StepEvidence = {\n name: string;\n startedAt: string;\n durationMs: number;\n status: \"passed\" | \"failed\";\n error?: string;\n};\nexport type ProcessEvidence = {\n role: string;\n pid: number;\n childPid?: number;\n command: string[];\n ownerFile: string;\n stopped?: boolean;\n};\nexport type AccessibilityOptions = {\n /** CSS selector limiting the scan; defaults to the document. */\n selector?: string;\n /** axe rule tags, for example wcag2a and wcag2aa. Defaults to all enabled rules. */\n tags?: string[];\n /** Exact rule ID, optionally limited to an exact reported selector. */\n allow?: { id: string; selector?: string }[];\n timeoutMs?: number;\n};\nexport type AccessibilityViolation = {\n id: string;\n impact: string | null;\n help: string;\n helpUrl: string;\n nodes: { target: (string | string[])[]; failureSummary?: string; allowed?: boolean }[];\n};\nexport type AccessibilityEvidence = {\n at: string;\n url: string;\n viewport: Viewport;\n violations: AccessibilityViolation[];\n incomplete: AccessibilityViolation[];\n};\n/**\n * The inline payload of one loaded document, in bytes as the HTML carries it:\n * the hydration data script and the browser route table. `limit` and\n * `overBudget` are present when the caller passed a limit to `measurePayload`.\n */\nexport type PayloadEvidence = {\n at: string;\n url: string;\n viewport: Viewport;\n /** The `__WARLOCK_DATA__` script: page, layout and app data, translations, session. */\n data: number;\n /** The `__WARLOCK_NAMED_API_ROUTES__` script: the page's browser route table. */\n routes: number;\n total: number;\n limit?: number;\n overBudget?: boolean;\n};\nexport type E2EEvidence = {\n schemaVersion: 1;\n startedAt: string;\n durationMs: number;\n status: \"running\" | \"passed\" | \"failed\";\n url?: string;\n steps: StepEvidence[];\n console: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n screenshots: { name: string; file: string; viewport: Viewport }[];\n accessibility?: AccessibilityEvidence[];\n payloads?: PayloadEvidence[];\n processes: ProcessEvidence[];\n errors: string[];\n};\nexport type NetworkAllowance = { url: Pattern; status?: number; errorText?: Pattern };\nexport type E2EContext = FixtureContext & {\n page: BrowserPage;\n evidence: E2EEvidence;\n outputDirectory: string;\n signal: AbortSignal;\n step: <T>(name: string, action: () => Promise<T>) => Promise<T>;\n};\nexport type E2ESpec = {\n app: AppFixtureOptions;\n browser?: BrowserOptions;\n viewports?: readonly Viewport[];\n allowConsoleErrors?: Pattern[];\n allowNetworkFailures?: NetworkAllowance[];\n /** Long-lived HTTP requests excluded from the final network-idle barrier. */\n allowPendingRequests?: Pattern[];\n timeoutMs?: number;\n run: (context: E2EContext) => Promise<void>;\n};\nexport type RunE2EOptions = {\n browser?: string;\n path?: string[];\n outputDirectory?: string;\n signal?: AbortSignal;\n};\n\nexport function defineE2E(spec: E2ESpec): E2ESpec {\n return spec;\n}\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\nexport function matches(pattern: Pattern, value: string): boolean {\n // A fresh regexp avoids stateful /g and /y allowlists changing their verdict.\n return typeof pattern === \"string\"\n ? value.includes(pattern)\n : new RegExp(pattern.source, pattern.flags).test(value);\n}\n"],"mappings":";AAKA,MAAa,mBAAwC,CACnD;CAAE,MAAM;CAAW,OAAO;CAAM,QAAQ;AAAI,GAC5C;CAAE,MAAM;CAAS,OAAO;CAAK,QAAQ;CAAK,QAAQ;AAAK,CACzD;AAmJA,SAAgB,UAAU,MAAwB;CAChD,OAAO;AACT;AACA,SAAgB,aAAa,OAAwB;CACnD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AACA,SAAgB,QAAQ,SAAkB,OAAwB;CAEhE,OAAO,OAAO,YAAY,WACtB,MAAM,SAAS,OAAO,IACtB,IAAI,OAAO,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC,KAAK,KAAK;AAC1D"}
|