qa360 2.2.20 → 2.3.1
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/README.md +155 -262
- package/{cli/dist → dist}/commands/ai.js +1 -1
- package/{cli/dist → dist}/commands/coverage.js +1 -1
- package/{cli/dist → dist}/commands/crawl.d.ts +12 -1
- package/{cli/dist → dist}/commands/crawl.js +70 -9
- package/{cli/dist → dist}/commands/doctor.js +2 -2
- package/{cli/dist → dist}/commands/explain.js +2 -2
- package/{cli/dist → dist}/commands/flakiness.js +1 -1
- package/{cli/dist → dist}/commands/generate.js +1 -1
- package/{cli/dist → dist}/commands/history.js +1 -1
- package/{cli/dist → dist}/commands/monitor.js +3 -3
- package/{cli/dist → dist}/commands/ollama.js +1 -1
- package/{cli/dist → dist}/commands/pack.js +2 -2
- package/{cli/dist → dist}/commands/regression.js +1 -1
- package/{cli/dist → dist}/commands/repair.js +1 -1
- package/{cli/dist → dist}/commands/retry.js +1 -1
- package/{cli/dist → dist}/commands/run.d.ts +1 -1
- package/{cli/dist → dist}/commands/run.js +1 -1
- package/{cli/dist → dist}/commands/secrets.js +1 -1
- package/{cli/dist → dist}/commands/serve.js +1 -1
- package/{cli/dist → dist}/commands/slo.js +1 -1
- package/{cli/dist → dist}/commands/verify.js +1 -1
- package/{cli/dist → dist}/core/adapters/playwright-native-api.d.ts +2 -0
- package/{cli/dist → dist}/core/adapters/playwright-native-api.js +20 -1
- package/{cli/dist → dist}/core/adapters/playwright-ui.d.ts +21 -0
- package/dist/core/adapters/playwright-ui.js +2050 -0
- package/{cli/dist → dist}/core/ai/ollama-provider.js +15 -3
- package/{cli/dist → dist}/core/artifacts/ui-artifacts.js +24 -4
- package/dist/core/auth/backup-codes-provider.d.ts +91 -0
- package/dist/core/auth/backup-codes-provider.js +215 -0
- package/{cli/dist → dist}/core/auth/basic-auth-provider.d.ts +6 -0
- package/{cli/dist → dist}/core/auth/basic-auth-provider.js +24 -6
- package/dist/core/auth/digest-auth-provider.d.ts +116 -0
- package/dist/core/auth/digest-auth-provider.js +244 -0
- package/dist/core/auth/hcaptcha-handler.d.ts +103 -0
- package/dist/core/auth/hcaptcha-handler.js +288 -0
- package/{cli/dist → dist}/core/auth/index.d.ts +81 -4
- package/{cli/dist → dist}/core/auth/index.js +15 -1
- package/dist/core/auth/oauth-handler.d.ts +408 -0
- package/dist/core/auth/oauth-handler.js +636 -0
- package/{cli/dist → dist}/core/auth/oauth2-provider.d.ts +9 -0
- package/dist/core/auth/oauth2-provider.js +227 -0
- package/dist/core/auth/otp-provider.d.ts +93 -0
- package/dist/core/auth/otp-provider.js +288 -0
- package/dist/core/auth/recaptcha-handler.d.ts +119 -0
- package/dist/core/auth/recaptcha-handler.js +301 -0
- package/dist/core/auth/remember-me-handler.d.ts +142 -0
- package/dist/core/auth/remember-me-handler.js +255 -0
- package/dist/core/auth/saml-handler.d.ts +173 -0
- package/dist/core/auth/saml-handler.js +364 -0
- package/dist/core/auth/webauthn-handler.d.ts +182 -0
- package/dist/core/auth/webauthn-handler.js +310 -0
- package/dist/core/crawler/advanced-interactions.d.ts +342 -0
- package/dist/core/crawler/advanced-interactions.js +1069 -0
- package/dist/core/crawler/blob-url-download-handler.d.ts +145 -0
- package/dist/core/crawler/blob-url-download-handler.js +392 -0
- package/dist/core/crawler/consent-handler.d.ts +49 -0
- package/dist/core/crawler/consent-handler.js +258 -0
- package/dist/core/crawler/cookie-manager.d.ts +166 -0
- package/dist/core/crawler/cookie-manager.js +353 -0
- package/dist/core/crawler/coop-coep-handler.d.ts +136 -0
- package/dist/core/crawler/coop-coep-handler.js +338 -0
- package/dist/core/crawler/csp-handler.d.ts +151 -0
- package/dist/core/crawler/csp-handler.js +415 -0
- package/dist/core/crawler/download-handler.d.ts +155 -0
- package/dist/core/crawler/download-handler.js +370 -0
- package/dist/core/crawler/email-testing-handler.d.ts +214 -0
- package/dist/core/crawler/email-testing-handler.js +398 -0
- package/dist/core/crawler/error-tracking-handler.d.ts +177 -0
- package/dist/core/crawler/error-tracking-handler.js +378 -0
- package/dist/core/crawler/form-handler.d.ts +100 -0
- package/dist/core/crawler/form-handler.js +465 -0
- package/dist/core/crawler/framework-wait-handler.d.ts +96 -0
- package/dist/core/crawler/framework-wait-handler.js +464 -0
- package/dist/core/crawler/geolocation-handler.d.ts +112 -0
- package/dist/core/crawler/geolocation-handler.js +276 -0
- package/dist/core/crawler/index.d.ts +78 -0
- package/{cli/dist → dist}/core/crawler/index.js +74 -1
- package/dist/core/crawler/intelligent-selector-generator.d.ts +164 -0
- package/dist/core/crawler/intelligent-selector-generator.js +612 -0
- package/{cli/dist → dist}/core/crawler/journey-generator.js +44 -1
- package/{cli/dist → dist}/core/crawler/page-analyzer.d.ts +16 -1
- package/{cli/dist → dist}/core/crawler/page-analyzer.js +469 -17
- package/dist/core/crawler/permissions-handler.d.ts +112 -0
- package/dist/core/crawler/permissions-handler.js +236 -0
- package/dist/core/crawler/permissions-policy-handler.d.ts +113 -0
- package/dist/core/crawler/permissions-policy-handler.js +402 -0
- package/dist/core/crawler/presets.d.ts +100 -0
- package/dist/core/crawler/presets.js +887 -0
- package/dist/core/crawler/repl-debug-handler.d.ts +105 -0
- package/dist/core/crawler/repl-debug-handler.js +552 -0
- package/dist/core/crawler/reporting-api-handler.d.ts +212 -0
- package/dist/core/crawler/reporting-api-handler.js +344 -0
- package/{cli/dist → dist}/core/crawler/selector-generator.d.ts +9 -0
- package/{cli/dist → dist}/core/crawler/selector-generator.js +99 -23
- package/dist/core/crawler/site-profiler.d.ts +89 -0
- package/dist/core/crawler/site-profiler.js +290 -0
- package/dist/core/crawler/sourcemaps-handler.d.ts +144 -0
- package/dist/core/crawler/sourcemaps-handler.js +420 -0
- package/dist/core/crawler/stacked-modals-handler.d.ts +118 -0
- package/dist/core/crawler/stacked-modals-handler.js +429 -0
- package/dist/core/crawler/trusted-types-handler.d.ts +149 -0
- package/dist/core/crawler/trusted-types-handler.js +413 -0
- package/{cli/dist → dist}/core/crawler/types.d.ts +68 -2
- package/dist/core/crawler/wait-strategies.d.ts +108 -0
- package/dist/core/crawler/wait-strategies.js +399 -0
- package/dist/core/fixtures/factories.d.ts +180 -0
- package/dist/core/fixtures/factories.js +279 -0
- package/dist/core/fixtures/index.d.ts +6 -0
- package/dist/core/fixtures/index.js +6 -0
- package/{cli/dist → dist}/core/generation/crawler-pack-generator.d.ts +13 -3
- package/dist/core/generation/crawler-pack-generator.js +232 -0
- package/{cli/dist → dist}/core/generation/index.d.ts +2 -0
- package/{cli/dist → dist}/core/generation/index.js +2 -0
- package/{cli/dist → dist}/core/index.d.ts +2 -0
- package/{cli/dist → dist}/core/index.js +4 -0
- package/dist/core/network/index.d.ts +7 -0
- package/dist/core/network/index.js +7 -0
- package/dist/core/network/network-manager.d.ts +237 -0
- package/dist/core/network/network-manager.js +343 -0
- package/dist/core/network/network-simulator.d.ts +158 -0
- package/dist/core/network/network-simulator.js +261 -0
- package/{cli/dist → dist}/core/pack/validator.js +2 -2
- package/{cli/dist → dist}/core/pack-v2/migrator.d.ts +5 -0
- package/{cli/dist → dist}/core/pack-v2/migrator.js +81 -6
- package/{cli/dist → dist}/core/pack-v2/validator.js +4 -3
- package/{cli/dist → dist}/core/pom/base-page.js +1 -1
- package/{cli/dist → dist}/core/pom/loader.js +1 -1
- package/dist/core/reporting/index.d.ts +9 -0
- package/dist/core/reporting/index.js +10 -0
- package/dist/core/reporting/junit-reporter.d.ts +114 -0
- package/dist/core/reporting/junit-reporter.js +306 -0
- package/{cli/dist → dist}/core/runner/e2e-helpers.d.ts +1 -1
- package/{cli/dist → dist}/core/runner/e2e-helpers.js +2 -2
- package/{cli/dist → dist}/core/runner/phase3-runner.d.ts +3 -0
- package/{cli/dist → dist}/core/runner/phase3-runner.js +45 -14
- package/dist/core/sharding/test-sharding.d.ts +137 -0
- package/dist/core/sharding/test-sharding.js +233 -0
- package/dist/core/storage/cookie-manager.d.ts +160 -0
- package/dist/core/storage/cookie-manager.js +268 -0
- package/dist/core/storage/index.d.ts +7 -0
- package/dist/core/storage/index.js +7 -0
- package/dist/core/storage/storage-helpers.d.ts +138 -0
- package/dist/core/storage/storage-helpers.js +315 -0
- package/dist/core/test-helpers/index.d.ts +6 -0
- package/dist/core/test-helpers/index.js +6 -0
- package/dist/core/test-helpers/state-reset.d.ts +119 -0
- package/dist/core/test-helpers/state-reset.js +234 -0
- package/{cli/dist → dist}/core/types/pack-v1.d.ts +15 -2
- package/{cli/dist → dist}/core/types/pack-v2.d.ts +1 -1
- package/dist/core/upload/chunked-uploader.d.ts +150 -0
- package/dist/core/upload/chunked-uploader.js +289 -0
- package/dist/core/upload/index.d.ts +11 -0
- package/dist/core/upload/index.js +8 -0
- package/dist/core/upload/mime-validator.d.ts +119 -0
- package/dist/core/upload/mime-validator.js +373 -0
- package/dist/core/upload/presigned-uploader.d.ts +118 -0
- package/dist/core/upload/presigned-uploader.js +274 -0
- package/dist/core/utils/device-emulation.d.ts +194 -0
- package/dist/core/utils/device-emulation.js +380 -0
- package/dist/core/utils/index.d.ts +8 -0
- package/dist/core/utils/index.js +8 -0
- package/dist/core/utils/retry.d.ts +145 -0
- package/dist/core/utils/retry.js +242 -0
- package/dist/core/utils/smart-wait.d.ts +133 -0
- package/dist/core/utils/smart-wait.js +417 -0
- package/dist/core/visual/index.d.ts +7 -0
- package/dist/core/visual/index.js +7 -0
- package/dist/core/visual/pixel-diff.d.ts +87 -0
- package/dist/core/visual/pixel-diff.js +213 -0
- package/dist/core/visual/screenshot-helper.d.ts +130 -0
- package/dist/core/visual/screenshot-helper.js +223 -0
- package/{cli/dist → dist}/utils/config.d.ts +1 -1
- package/examples/README.md +160 -0
- package/examples/accessibility.yml +48 -0
- package/examples/api-basic.yml +27 -0
- package/examples/complete.yml +146 -0
- package/examples/crawler.yml +38 -0
- package/examples/fullstack.yml +78 -0
- package/examples/security.yml +58 -0
- package/examples/ui-advanced.yml +49 -0
- package/examples/ui-basic.yml +24 -0
- package/package.json +33 -67
- package/CHANGELOG.md +0 -262
- package/CONTRIBUTING.md +0 -273
- package/QUICK_START.md +0 -191
- package/cli/CHANGELOG.md +0 -84
- package/cli/LICENSE +0 -24
- package/cli/README.md +0 -222
- package/cli/dist/core/adapters/playwright-ui.js +0 -864
- package/cli/dist/core/auth/oauth2-provider.js +0 -114
- package/cli/dist/core/coverage/analyzer.d.ts +0 -101
- package/cli/dist/core/coverage/analyzer.js +0 -415
- package/cli/dist/core/coverage/collector.d.ts +0 -74
- package/cli/dist/core/coverage/collector.js +0 -459
- package/cli/dist/core/coverage/config.d.ts +0 -37
- package/cli/dist/core/coverage/config.js +0 -156
- package/cli/dist/core/coverage/index.d.ts +0 -11
- package/cli/dist/core/coverage/index.js +0 -15
- package/cli/dist/core/coverage/types.d.ts +0 -267
- package/cli/dist/core/coverage/types.js +0 -6
- package/cli/dist/core/coverage/vault.d.ts +0 -95
- package/cli/dist/core/coverage/vault.js +0 -405
- package/cli/dist/core/crawler/index.d.ts +0 -57
- package/cli/dist/core/fixtures/index.d.ts +0 -8
- package/cli/dist/core/fixtures/index.js +0 -8
- package/cli/dist/core/generation/crawler-pack-generator.js +0 -231
- package/cli/dist/core/reporting/index.d.ts +0 -6
- package/cli/dist/core/reporting/index.js +0 -6
- package/cli/dist/core/visual/index.d.ts +0 -6
- package/cli/dist/core/visual/index.js +0 -6
- package/cli/package.json +0 -76
- package/core/LICENSE +0 -24
- package/core/README.md +0 -64
- package/core/package.json +0 -81
- package/core/schemas/pack.schema.json +0 -236
- /package/{cli/bin → bin}/qa360.js +0 -0
- /package/{cli/dist → dist}/cli-minimal.d.ts +0 -0
- /package/{cli/dist → dist}/cli-minimal.js +0 -0
- /package/{cli/dist → dist}/commands/ai.d.ts +0 -0
- /package/{cli/dist → dist}/commands/ask.d.ts +0 -0
- /package/{cli/dist → dist}/commands/ask.js +0 -0
- /package/{cli/dist → dist}/commands/coverage.d.ts +0 -0
- /package/{cli/dist → dist}/commands/doctor.d.ts +0 -0
- /package/{cli/dist → dist}/commands/examples.d.ts +0 -0
- /package/{cli/dist → dist}/commands/examples.js +0 -0
- /package/{cli/dist → dist}/commands/explain.d.ts +0 -0
- /package/{cli/dist → dist}/commands/flakiness.d.ts +0 -0
- /package/{cli/dist → dist}/commands/generate.d.ts +0 -0
- /package/{cli/dist → dist}/commands/history.d.ts +0 -0
- /package/{cli/dist → dist}/commands/init.d.ts +0 -0
- /package/{cli/dist → dist}/commands/init.js +0 -0
- /package/{cli/dist → dist}/commands/monitor.d.ts +0 -0
- /package/{cli/dist → dist}/commands/ollama.d.ts +0 -0
- /package/{cli/dist → dist}/commands/pack.d.ts +0 -0
- /package/{cli/dist → dist}/commands/regression.d.ts +0 -0
- /package/{cli/dist → dist}/commands/repair.d.ts +0 -0
- /package/{cli/dist → dist}/commands/report.d.ts +0 -0
- /package/{cli/dist → dist}/commands/report.js +0 -0
- /package/{cli/dist → dist}/commands/retry.d.ts +0 -0
- /package/{cli/dist → dist}/commands/scan.d.ts +0 -0
- /package/{cli/dist → dist}/commands/scan.js +0 -0
- /package/{cli/dist → dist}/commands/secrets.d.ts +0 -0
- /package/{cli/dist → dist}/commands/serve.d.ts +0 -0
- /package/{cli/dist → dist}/commands/slo.d.ts +0 -0
- /package/{cli/dist → dist}/commands/verify.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/gitleaks-secrets.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/gitleaks-secrets.js +0 -0
- /package/{cli/dist → dist}/core/adapters/jest-adapter.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/jest-adapter.js +0 -0
- /package/{cli/dist → dist}/core/adapters/k6-perf.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/k6-perf.js +0 -0
- /package/{cli/dist → dist}/core/adapters/osv-deps.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/osv-deps.js +0 -0
- /package/{cli/dist → dist}/core/adapters/playwright-native-adapter.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/playwright-native-adapter.js +0 -0
- /package/{cli/dist → dist}/core/adapters/pytest-adapter.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/pytest-adapter.js +0 -0
- /package/{cli/dist → dist}/core/adapters/semgrep-sast.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/semgrep-sast.js +0 -0
- /package/{cli/dist → dist}/core/adapters/unit-test-types.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/unit-test-types.js +0 -0
- /package/{cli/dist → dist}/core/adapters/vitest-adapter.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/vitest-adapter.js +0 -0
- /package/{cli/dist → dist}/core/adapters/zap-dast.d.ts +0 -0
- /package/{cli/dist → dist}/core/adapters/zap-dast.js +0 -0
- /package/{cli/dist → dist}/core/ai/anthropic-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/anthropic-provider.js +0 -0
- /package/{cli/dist → dist}/core/ai/deepseek-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/deepseek-provider.js +0 -0
- /package/{cli/dist → dist}/core/ai/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/index.js +0 -0
- /package/{cli/dist → dist}/core/ai/llm-client.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/llm-client.js +0 -0
- /package/{cli/dist → dist}/core/ai/mock-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/mock-provider.js +0 -0
- /package/{cli/dist → dist}/core/ai/ollama-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/openai-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/openai-provider.js +0 -0
- /package/{cli/dist → dist}/core/ai/provider-factory.d.ts +0 -0
- /package/{cli/dist → dist}/core/ai/provider-factory.js +0 -0
- /package/{cli/dist → dist}/core/artifacts/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/artifacts/index.js +0 -0
- /package/{cli/dist → dist}/core/artifacts/ui-artifacts.d.ts +0 -0
- /package/{cli/dist → dist}/core/assertions/engine.d.ts +0 -0
- /package/{cli/dist → dist}/core/assertions/engine.js +0 -0
- /package/{cli/dist → dist}/core/assertions/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/assertions/index.js +0 -0
- /package/{cli/dist → dist}/core/assertions/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/assertions/types.js +0 -0
- /package/{cli/dist → dist}/core/auth/api-key-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/api-key-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/aws-iam-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/aws-iam-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/azure-ad-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/azure-ad-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/gcp-adc-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/gcp-adc-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/jwt-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/jwt-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/manager.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/manager.js +0 -0
- /package/{cli/dist → dist}/core/auth/totp-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/totp-provider.js +0 -0
- /package/{cli/dist → dist}/core/auth/ui-login-provider.d.ts +0 -0
- /package/{cli/dist → dist}/core/auth/ui-login-provider.js +0 -0
- /package/{cli/dist → dist}/core/cache/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/cache/index.js +0 -0
- /package/{cli/dist → dist}/core/cache/lru-cache.d.ts +0 -0
- /package/{cli/dist → dist}/core/cache/lru-cache.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/analyzer.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/analyzer.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/collector.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/collector.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/config.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/config.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/index.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/index.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/types.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/types.js +0 -0
- /package/{cli/dist/core → dist}/core/coverage/vault.d.ts +0 -0
- /package/{cli/dist/core → dist}/core/coverage/vault.js +0 -0
- /package/{cli/dist → dist}/core/crawler/journey-generator.d.ts +0 -0
- /package/{cli/dist → dist}/core/crawler/types.js +0 -0
- /package/{cli/dist → dist}/core/dashboard/assets.d.ts +0 -0
- /package/{cli/dist → dist}/core/dashboard/assets.js +0 -0
- /package/{cli/dist → dist}/core/dashboard/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/dashboard/index.js +0 -0
- /package/{cli/dist → dist}/core/dashboard/server.d.ts +0 -0
- /package/{cli/dist → dist}/core/dashboard/server.js +0 -0
- /package/{cli/dist → dist}/core/dashboard/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/dashboard/types.js +0 -0
- /package/{cli/dist → dist}/core/discoverer/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/discoverer/index.js +0 -0
- /package/{cli/dist → dist}/core/fixtures/loader.d.ts +0 -0
- /package/{cli/dist → dist}/core/fixtures/loader.js +0 -0
- /package/{cli/dist → dist}/core/fixtures/resolver.d.ts +0 -0
- /package/{cli/dist → dist}/core/fixtures/resolver.js +0 -0
- /package/{cli/dist → dist}/core/fixtures/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/fixtures/types.js +0 -0
- /package/{cli/dist → dist}/core/flakiness/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/flakiness/index.js +0 -0
- /package/{cli/dist → dist}/core/generation/code-formatter.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/code-formatter.js +0 -0
- /package/{cli/dist → dist}/core/generation/code-generator.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/code-generator.js +0 -0
- /package/{cli/dist → dist}/core/generation/generator.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/generator.js +0 -0
- /package/{cli/dist → dist}/core/generation/pack-generator.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/pack-generator.js +0 -0
- /package/{cli/dist → dist}/core/generation/prompt-builder.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/prompt-builder.js +0 -0
- /package/{cli/dist → dist}/core/generation/source-analyzer.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/source-analyzer.js +0 -0
- /package/{cli/dist → dist}/core/generation/test-optimizer.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/test-optimizer.js +0 -0
- /package/{cli/dist → dist}/core/generation/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/generation/types.js +0 -0
- /package/{cli/dist → dist}/core/hooks/compose.d.ts +0 -0
- /package/{cli/dist → dist}/core/hooks/compose.js +0 -0
- /package/{cli/dist → dist}/core/hooks/runner.d.ts +0 -0
- /package/{cli/dist → dist}/core/hooks/runner.js +0 -0
- /package/{cli/dist → dist}/core/pack/migrator.d.ts +0 -0
- /package/{cli/dist → dist}/core/pack/migrator.js +0 -0
- /package/{cli/dist → dist}/core/pack/validator.d.ts +0 -0
- /package/{cli/dist → dist}/core/pack-v2/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/pack-v2/index.js +0 -0
- /package/{cli/dist → dist}/core/pack-v2/loader.d.ts +0 -0
- /package/{cli/dist → dist}/core/pack-v2/loader.js +0 -0
- /package/{cli/dist → dist}/core/pack-v2/validator.d.ts +0 -0
- /package/{cli/dist → dist}/core/parallel/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/parallel/index.js +0 -0
- /package/{cli/dist → dist}/core/parallel/parallel-runner.d.ts +0 -0
- /package/{cli/dist → dist}/core/parallel/parallel-runner.js +0 -0
- /package/{cli/dist → dist}/core/pom/base-page.d.ts +0 -0
- /package/{cli/dist → dist}/core/pom/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/pom/index.js +0 -0
- /package/{cli/dist → dist}/core/pom/loader.d.ts +0 -0
- /package/{cli/dist → dist}/core/pom/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/pom/types.js +0 -0
- /package/{cli/dist → dist}/core/proof/bundle.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/bundle.js +0 -0
- /package/{cli/dist → dist}/core/proof/canonicalize.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/canonicalize.js +0 -0
- /package/{cli/dist → dist}/core/proof/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/index.js +0 -0
- /package/{cli/dist → dist}/core/proof/schema.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/schema.js +0 -0
- /package/{cli/dist → dist}/core/proof/signer.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/signer.js +0 -0
- /package/{cli/dist → dist}/core/proof/verifier.d.ts +0 -0
- /package/{cli/dist → dist}/core/proof/verifier.js +0 -0
- /package/{cli/dist → dist}/core/regression/detector.d.ts +0 -0
- /package/{cli/dist → dist}/core/regression/detector.js +0 -0
- /package/{cli/dist → dist}/core/regression/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/regression/index.js +0 -0
- /package/{cli/dist → dist}/core/regression/trend-analyzer.d.ts +0 -0
- /package/{cli/dist → dist}/core/regression/trend-analyzer.js +0 -0
- /package/{cli/dist → dist}/core/regression/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/regression/types.js +0 -0
- /package/{cli/dist → dist}/core/regression/vault.d.ts +0 -0
- /package/{cli/dist → dist}/core/regression/vault.js +0 -0
- /package/{cli/dist → dist}/core/repair/engine/fixer.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/engine/fixer.js +0 -0
- /package/{cli/dist → dist}/core/repair/engine/suggestion-engine.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/engine/suggestion-engine.js +0 -0
- /package/{cli/dist → dist}/core/repair/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/index.js +0 -0
- /package/{cli/dist → dist}/core/repair/repairer.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/repairer.js +0 -0
- /package/{cli/dist → dist}/core/repair/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/types.js +0 -0
- /package/{cli/dist → dist}/core/repair/utils/error-analyzer.d.ts +0 -0
- /package/{cli/dist → dist}/core/repair/utils/error-analyzer.js +0 -0
- /package/{cli/dist → dist}/core/reporting/html-reporter.d.ts +0 -0
- /package/{cli/dist → dist}/core/reporting/html-reporter.js +0 -0
- /package/{cli/dist → dist}/core/retry/flakiness-integration.d.ts +0 -0
- /package/{cli/dist → dist}/core/retry/flakiness-integration.js +0 -0
- /package/{cli/dist → dist}/core/retry/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/retry/index.js +0 -0
- /package/{cli/dist → dist}/core/retry/retry-engine.d.ts +0 -0
- /package/{cli/dist → dist}/core/retry/retry-engine.js +0 -0
- /package/{cli/dist → dist}/core/retry/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/retry/types.js +0 -0
- /package/{cli/dist → dist}/core/retry/vault.d.ts +0 -0
- /package/{cli/dist → dist}/core/retry/vault.js +0 -0
- /package/{cli/dist → dist}/core/schemas/pack.schema.json +0 -0
- /package/{cli/dist → dist}/core/secrets/crypto.d.ts +0 -0
- /package/{cli/dist → dist}/core/secrets/crypto.js +0 -0
- /package/{cli/dist → dist}/core/secrets/manager.d.ts +0 -0
- /package/{cli/dist → dist}/core/secrets/manager.js +0 -0
- /package/{cli/dist → dist}/core/security/redaction-patterns-extended.d.ts +0 -0
- /package/{cli/dist → dist}/core/security/redaction-patterns-extended.js +0 -0
- /package/{cli/dist → dist}/core/security/redactor.d.ts +0 -0
- /package/{cli/dist → dist}/core/security/redactor.js +0 -0
- /package/{cli/dist → dist}/core/self-healing/assertion-healer.d.ts +0 -0
- /package/{cli/dist → dist}/core/self-healing/assertion-healer.js +0 -0
- /package/{cli/dist → dist}/core/self-healing/engine.d.ts +0 -0
- /package/{cli/dist → dist}/core/self-healing/engine.js +0 -0
- /package/{cli/dist → dist}/core/self-healing/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/self-healing/index.js +0 -0
- /package/{cli/dist → dist}/core/self-healing/selector-healer.d.ts +0 -0
- /package/{cli/dist → dist}/core/self-healing/selector-healer.js +0 -0
- /package/{cli/dist → dist}/core/self-healing/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/self-healing/types.js +0 -0
- /package/{cli/dist → dist}/core/serve/diagnostics-collector.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/diagnostics-collector.js +0 -0
- /package/{cli/dist → dist}/core/serve/health-checker.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/health-checker.js +0 -0
- /package/{cli/dist → dist}/core/serve/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/index.js +0 -0
- /package/{cli/dist → dist}/core/serve/metrics-collector.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/metrics-collector.js +0 -0
- /package/{cli/dist → dist}/core/serve/process-manager.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/process-manager.js +0 -0
- /package/{cli/dist → dist}/core/serve/server.d.ts +0 -0
- /package/{cli/dist → dist}/core/serve/server.js +0 -0
- /package/{cli/dist → dist}/core/slo/config.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/config.js +0 -0
- /package/{cli/dist → dist}/core/slo/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/index.js +0 -0
- /package/{cli/dist → dist}/core/slo/sli-calculator.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/sli-calculator.js +0 -0
- /package/{cli/dist → dist}/core/slo/slo-tracker.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/slo-tracker.js +0 -0
- /package/{cli/dist → dist}/core/slo/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/types.js +0 -0
- /package/{cli/dist → dist}/core/slo/vault.d.ts +0 -0
- /package/{cli/dist → dist}/core/slo/vault.js +0 -0
- /package/{cli/dist → dist}/core/tui/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/tui/index.js +0 -0
- /package/{cli/dist → dist}/core/tui/monitor.d.ts +0 -0
- /package/{cli/dist → dist}/core/tui/monitor.js +0 -0
- /package/{cli/dist → dist}/core/tui/renderer.d.ts +0 -0
- /package/{cli/dist → dist}/core/tui/renderer.js +0 -0
- /package/{cli/dist → dist}/core/tui/types.d.ts +0 -0
- /package/{cli/dist → dist}/core/tui/types.js +0 -0
- /package/{cli/dist → dist}/core/types/pack-v1.js +0 -0
- /package/{cli/dist → dist}/core/types/pack-v2.js +0 -0
- /package/{cli/dist → dist}/core/types/trust-score.d.ts +0 -0
- /package/{cli/dist → dist}/core/types/trust-score.js +0 -0
- /package/{cli/dist → dist}/core/vault/cas.d.ts +0 -0
- /package/{cli/dist → dist}/core/vault/cas.js +0 -0
- /package/{cli/dist → dist}/core/vault/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/vault/index.js +0 -0
- /package/{cli/dist → dist}/core/visual/visual-regression.d.ts +0 -0
- /package/{cli/dist → dist}/core/visual/visual-regression.js +0 -0
- /package/{cli/dist → dist}/core/watch/index.d.ts +0 -0
- /package/{cli/dist → dist}/core/watch/index.js +0 -0
- /package/{cli/dist → dist}/core/watch/watch-mode.d.ts +0 -0
- /package/{cli/dist → dist}/core/watch/watch-mode.js +0 -0
- /package/{cli/dist → dist}/generators/index.d.ts +0 -0
- /package/{cli/dist → dist}/generators/index.js +0 -0
- /package/{cli/dist → dist}/generators/json-reporter.d.ts +0 -0
- /package/{cli/dist → dist}/generators/json-reporter.js +0 -0
- /package/{cli/dist → dist}/generators/test-generator.d.ts +0 -0
- /package/{cli/dist → dist}/generators/test-generator.js +0 -0
- /package/{cli/dist → dist}/index.d.ts +0 -0
- /package/{cli/dist → dist}/index.js +0 -0
- /package/{cli/dist → dist}/scanners/dom-scanner.d.ts +0 -0
- /package/{cli/dist → dist}/scanners/dom-scanner.js +0 -0
- /package/{cli/dist → dist}/scanners/index.d.ts +0 -0
- /package/{cli/dist → dist}/scanners/index.js +0 -0
- /package/{cli/dist → dist}/schemas/pack.schema.json +0 -0
- /package/{cli/dist → dist}/types/scan.d.ts +0 -0
- /package/{cli/dist → dist}/types/scan.js +0 -0
- /package/{cli/dist → dist}/utils/config.js +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blob URL Download Handler
|
|
3
|
+
*
|
|
4
|
+
* P1 - Handle downloads initiated via createObjectURL / URL.createObjectURL
|
|
5
|
+
*
|
|
6
|
+
* Supports:
|
|
7
|
+
* - Detection of blob URL downloads
|
|
8
|
+
* - Intercepting blob URL creation
|
|
9
|
+
* - Capturing blob content
|
|
10
|
+
* - Validating blob downloads
|
|
11
|
+
* - Converting blobs to files
|
|
12
|
+
*
|
|
13
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
|
|
14
|
+
*/
|
|
15
|
+
export interface BlobDownloadConfig {
|
|
16
|
+
/** Directory to save blob downloads */
|
|
17
|
+
downloadDir?: string;
|
|
18
|
+
/** Whether to capture blob content automatically */
|
|
19
|
+
captureContent?: boolean;
|
|
20
|
+
/** Maximum blob size to capture (bytes) */
|
|
21
|
+
maxCaptureSize?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BlobDownloadInfo {
|
|
24
|
+
/** Blob URL */
|
|
25
|
+
url: string;
|
|
26
|
+
/** Blob ID (internal) */
|
|
27
|
+
blobId: string;
|
|
28
|
+
/** MIME type */
|
|
29
|
+
mimeType: string;
|
|
30
|
+
/** Size in bytes */
|
|
31
|
+
size: number;
|
|
32
|
+
/** Content (if captured) */
|
|
33
|
+
content?: Buffer;
|
|
34
|
+
/** Timestamp */
|
|
35
|
+
timestamp: number;
|
|
36
|
+
/** Source element that triggered download */
|
|
37
|
+
source?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface BlobDownloadResult {
|
|
40
|
+
success: boolean;
|
|
41
|
+
blob?: BlobDownloadInfo;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Blob URL Download Handler class
|
|
46
|
+
*/
|
|
47
|
+
export declare class BlobURLDownloadHandler {
|
|
48
|
+
private downloads;
|
|
49
|
+
private downloadDir;
|
|
50
|
+
private captureContent;
|
|
51
|
+
private maxCaptureSize;
|
|
52
|
+
private stats;
|
|
53
|
+
constructor(config?: BlobDownloadConfig);
|
|
54
|
+
private ensureDownloadDir;
|
|
55
|
+
/**
|
|
56
|
+
* Setup blob URL interception on a page
|
|
57
|
+
* Overrides URL.createObjectURL to capture blob creation
|
|
58
|
+
*/
|
|
59
|
+
setupBlobInterception(page: any): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Wait for blob URL creation matching a pattern
|
|
62
|
+
*/
|
|
63
|
+
waitForBlob(page: any, options?: {
|
|
64
|
+
/** MIME type to match */
|
|
65
|
+
mimeType?: string | RegExp;
|
|
66
|
+
/** Minimum size */
|
|
67
|
+
minSize?: number;
|
|
68
|
+
/** Maximum size */
|
|
69
|
+
maxSize?: number;
|
|
70
|
+
/** Timeout in ms */
|
|
71
|
+
timeout?: number;
|
|
72
|
+
}): Promise<BlobDownloadResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Trigger a blob download by clicking an element
|
|
75
|
+
*/
|
|
76
|
+
clickAndWaitForBlob(page: any, selector: string, options?: {
|
|
77
|
+
mimeType?: string | RegExp;
|
|
78
|
+
timeout?: number;
|
|
79
|
+
action?: 'click' | 'dblclick';
|
|
80
|
+
}): Promise<BlobDownloadResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Save a blob to a file
|
|
83
|
+
*/
|
|
84
|
+
saveBlob(blob: BlobDownloadInfo, filename?: string): Promise<string>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all captured blobs
|
|
87
|
+
*/
|
|
88
|
+
getAllBlobs(): BlobDownloadInfo[];
|
|
89
|
+
/**
|
|
90
|
+
* Get blob by ID
|
|
91
|
+
*/
|
|
92
|
+
getBlob(blobId: string): BlobDownloadInfo | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Get the last blob
|
|
95
|
+
*/
|
|
96
|
+
getLastBlob(): BlobDownloadInfo | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Get all blobs by MIME type
|
|
99
|
+
*/
|
|
100
|
+
getBlobsByMimeType(mimeType: string | RegExp): BlobDownloadInfo[];
|
|
101
|
+
/**
|
|
102
|
+
* Clear all captured blobs
|
|
103
|
+
*/
|
|
104
|
+
clearBlobs(): void;
|
|
105
|
+
/**
|
|
106
|
+
* Get file extension from MIME type
|
|
107
|
+
*/
|
|
108
|
+
private getExtension;
|
|
109
|
+
/**
|
|
110
|
+
* Get statistics
|
|
111
|
+
*/
|
|
112
|
+
getStats(): {
|
|
113
|
+
totalBlobs: number;
|
|
114
|
+
capturedBlobs: number;
|
|
115
|
+
totalSize: number;
|
|
116
|
+
averageSize: number;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Reset statistics
|
|
120
|
+
*/
|
|
121
|
+
resetStats(): void;
|
|
122
|
+
/**
|
|
123
|
+
* Get download directory
|
|
124
|
+
*/
|
|
125
|
+
getDownloadDir(): string;
|
|
126
|
+
/**
|
|
127
|
+
* Verify blob content
|
|
128
|
+
*/
|
|
129
|
+
verifyBlob(blob: BlobDownloadInfo, verification: {
|
|
130
|
+
/** Expected content pattern */
|
|
131
|
+
contentPattern?: string | RegExp;
|
|
132
|
+
/** Minimum size */
|
|
133
|
+
minSize?: number;
|
|
134
|
+
/** Maximum size */
|
|
135
|
+
maxSize?: number;
|
|
136
|
+
}): Promise<{
|
|
137
|
+
valid: boolean;
|
|
138
|
+
errors: string[];
|
|
139
|
+
}>;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Factory function to create Blob URL Download Handler
|
|
143
|
+
*/
|
|
144
|
+
export declare function createBlobURLDownloadHandler(config?: BlobDownloadConfig): BlobURLDownloadHandler;
|
|
145
|
+
export default BlobURLDownloadHandler;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blob URL Download Handler
|
|
3
|
+
*
|
|
4
|
+
* P1 - Handle downloads initiated via createObjectURL / URL.createObjectURL
|
|
5
|
+
*
|
|
6
|
+
* Supports:
|
|
7
|
+
* - Detection of blob URL downloads
|
|
8
|
+
* - Intercepting blob URL creation
|
|
9
|
+
* - Capturing blob content
|
|
10
|
+
* - Validating blob downloads
|
|
11
|
+
* - Converting blobs to files
|
|
12
|
+
*
|
|
13
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
|
|
14
|
+
*/
|
|
15
|
+
import { promises as fs } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
/**
|
|
18
|
+
* Blob URL Download Handler class
|
|
19
|
+
*/
|
|
20
|
+
export class BlobURLDownloadHandler {
|
|
21
|
+
downloads = new Map();
|
|
22
|
+
downloadDir;
|
|
23
|
+
captureContent;
|
|
24
|
+
maxCaptureSize;
|
|
25
|
+
stats = {
|
|
26
|
+
totalBlobs: 0,
|
|
27
|
+
capturedBlobs: 0,
|
|
28
|
+
totalSize: 0,
|
|
29
|
+
};
|
|
30
|
+
constructor(config = {}) {
|
|
31
|
+
this.downloadDir = config.downloadDir || '/tmp/qa360-blob-downloads';
|
|
32
|
+
this.captureContent = config.captureContent !== false;
|
|
33
|
+
this.maxCaptureSize = config.maxCaptureSize || 10 * 1024 * 1024; // 10MB default
|
|
34
|
+
this.ensureDownloadDir();
|
|
35
|
+
}
|
|
36
|
+
async ensureDownloadDir() {
|
|
37
|
+
try {
|
|
38
|
+
await fs.mkdir(this.downloadDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Directory might already exist
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Setup blob URL interception on a page
|
|
46
|
+
* Overrides URL.createObjectURL to capture blob creation
|
|
47
|
+
*/
|
|
48
|
+
async setupBlobInterception(page) {
|
|
49
|
+
await page.evaluate((config) => {
|
|
50
|
+
// Store original createObjectURL
|
|
51
|
+
const originalCreateObjectURL = URL.createObjectURL;
|
|
52
|
+
// Initialize blob storage on window
|
|
53
|
+
if (!window.__qa360Blobs) {
|
|
54
|
+
window.__qa360Blobs = [];
|
|
55
|
+
}
|
|
56
|
+
// Override URL.createObjectURL
|
|
57
|
+
URL.createObjectURL = function (blob) {
|
|
58
|
+
const url = originalCreateObjectURL.call(URL, blob);
|
|
59
|
+
// Capture blob info
|
|
60
|
+
const blobInfo = {
|
|
61
|
+
url,
|
|
62
|
+
id: `blob-${Date.now()}-${Math.random().toString(36).substring(7)}`,
|
|
63
|
+
mimeType: blob.type || 'application/octet-stream',
|
|
64
|
+
size: blob.size || 0,
|
|
65
|
+
timestamp: Date.now(),
|
|
66
|
+
};
|
|
67
|
+
// Capture content if enabled and size is reasonable
|
|
68
|
+
if (config.capture && blob.size <= config.maxSize) {
|
|
69
|
+
const reader = new FileReader();
|
|
70
|
+
reader.onload = () => {
|
|
71
|
+
blobInfo.content = reader.result;
|
|
72
|
+
};
|
|
73
|
+
reader.readAsDataURL(blob);
|
|
74
|
+
}
|
|
75
|
+
window.__qa360Blobs.push(blobInfo);
|
|
76
|
+
console.log(`📦 Blob created: ${blobInfo.mimeType} (${blobInfo.size} bytes)`);
|
|
77
|
+
return url;
|
|
78
|
+
};
|
|
79
|
+
// Also override URL.revokeObjectURL to track cleanup
|
|
80
|
+
const originalRevokeObjectURL = URL.revokeObjectURL;
|
|
81
|
+
URL.revokeObjectURL = function (url) {
|
|
82
|
+
originalRevokeObjectURL.call(URL, url);
|
|
83
|
+
const blobs = window.__qa360Blobs || [];
|
|
84
|
+
const index = blobs.findIndex((b) => b.url === url);
|
|
85
|
+
if (index !== -1) {
|
|
86
|
+
blobs[index].revoked = true;
|
|
87
|
+
blobs[index].revokedAt = Date.now();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Track blob downloads via download attribute on links
|
|
91
|
+
document.addEventListener('click', (e) => {
|
|
92
|
+
const target = e.target;
|
|
93
|
+
if (target.tagName === 'A' && target instanceof HTMLAnchorElement) {
|
|
94
|
+
const href = target.href;
|
|
95
|
+
if (href && href.startsWith('blob:')) {
|
|
96
|
+
const blobs = window.__qa360Blobs || [];
|
|
97
|
+
const blob = blobs.find((b) => b.url === href);
|
|
98
|
+
if (blob) {
|
|
99
|
+
blob.downloaded = true;
|
|
100
|
+
blob.downloadSource = {
|
|
101
|
+
tagName: target.tagName,
|
|
102
|
+
id: target.id,
|
|
103
|
+
className: target.className,
|
|
104
|
+
textContent: target.textContent?.substring(0, 50),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, true);
|
|
110
|
+
}, {
|
|
111
|
+
capture: this.captureContent,
|
|
112
|
+
maxSize: this.maxCaptureSize,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Wait for blob URL creation matching a pattern
|
|
117
|
+
*/
|
|
118
|
+
async waitForBlob(page, options = {}) {
|
|
119
|
+
const timeout = options.timeout || 30000;
|
|
120
|
+
const startTime = Date.now();
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
const checkInterval = setInterval(async () => {
|
|
123
|
+
try {
|
|
124
|
+
const blobs = await page.evaluate(() => {
|
|
125
|
+
return window.__qa360Blobs || [];
|
|
126
|
+
});
|
|
127
|
+
for (const blob of blobs) {
|
|
128
|
+
// Check MIME type filter
|
|
129
|
+
if (options.mimeType) {
|
|
130
|
+
const matches = typeof options.mimeType === 'string'
|
|
131
|
+
? blob.mimeType.includes(options.mimeType)
|
|
132
|
+
: options.mimeType.test(blob.mimeType);
|
|
133
|
+
if (!matches)
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Check size filters
|
|
137
|
+
if (options.minSize && blob.size < options.minSize)
|
|
138
|
+
continue;
|
|
139
|
+
if (options.maxSize && blob.size > options.maxSize)
|
|
140
|
+
continue;
|
|
141
|
+
clearInterval(checkInterval);
|
|
142
|
+
// Fetch blob content
|
|
143
|
+
let content;
|
|
144
|
+
if (this.captureContent) {
|
|
145
|
+
try {
|
|
146
|
+
const contentDataUrl = await page.evaluate((blobUrl) => {
|
|
147
|
+
const blobs = window.__qa360Blobs || [];
|
|
148
|
+
const blob = blobs.find((b) => b.url === blobUrl);
|
|
149
|
+
return blob?.content;
|
|
150
|
+
}, blob.url);
|
|
151
|
+
if (contentDataUrl) {
|
|
152
|
+
const base64 = contentDataUrl.split(',')[1];
|
|
153
|
+
content = Buffer.from(base64, 'base64');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Content not available
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const downloadInfo = {
|
|
161
|
+
url: blob.url,
|
|
162
|
+
blobId: blob.id,
|
|
163
|
+
mimeType: blob.mimeType,
|
|
164
|
+
size: blob.size,
|
|
165
|
+
content,
|
|
166
|
+
timestamp: blob.timestamp,
|
|
167
|
+
source: blob.downloadSource?.textContent,
|
|
168
|
+
};
|
|
169
|
+
this.downloads.set(blob.id, downloadInfo);
|
|
170
|
+
this.stats.totalBlobs++;
|
|
171
|
+
if (content) {
|
|
172
|
+
this.stats.capturedBlobs++;
|
|
173
|
+
this.stats.totalSize += content.length;
|
|
174
|
+
}
|
|
175
|
+
resolve({
|
|
176
|
+
success: true,
|
|
177
|
+
blob: downloadInfo,
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (Date.now() - startTime > timeout) {
|
|
182
|
+
clearInterval(checkInterval);
|
|
183
|
+
resolve({
|
|
184
|
+
success: false,
|
|
185
|
+
error: `Timeout waiting for blob URL creation`,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
clearInterval(checkInterval);
|
|
191
|
+
resolve({
|
|
192
|
+
success: false,
|
|
193
|
+
error: e instanceof Error ? e.message : String(e),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}, 100);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Trigger a blob download by clicking an element
|
|
201
|
+
*/
|
|
202
|
+
async clickAndWaitForBlob(page, selector, options = {}) {
|
|
203
|
+
await this.setupBlobInterception(page);
|
|
204
|
+
const timeout = options.timeout || 30000;
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
// Start waiting for blob before clicking
|
|
207
|
+
const blobPromise = this.waitForBlob(page, {
|
|
208
|
+
mimeType: options.mimeType,
|
|
209
|
+
timeout,
|
|
210
|
+
});
|
|
211
|
+
// Perform the click
|
|
212
|
+
try {
|
|
213
|
+
const element = page.locator(selector).first();
|
|
214
|
+
await element.waitFor({ state: 'visible', timeout: 5000 });
|
|
215
|
+
if (options.action === 'dblclick') {
|
|
216
|
+
await element.dblclick();
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
await element.click();
|
|
220
|
+
}
|
|
221
|
+
const result = await blobPromise;
|
|
222
|
+
// Add source info
|
|
223
|
+
if (result.blob) {
|
|
224
|
+
const sourceInfo = await page.evaluate((sel) => {
|
|
225
|
+
const el = document.querySelector(sel);
|
|
226
|
+
if (!el)
|
|
227
|
+
return null;
|
|
228
|
+
return {
|
|
229
|
+
tagName: el.tagName,
|
|
230
|
+
id: el.id,
|
|
231
|
+
className: el.className,
|
|
232
|
+
textContent: el.textContent?.substring(0, 50),
|
|
233
|
+
};
|
|
234
|
+
}, selector);
|
|
235
|
+
result.blob.source = sourceInfo?.textContent || selector;
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
error: e instanceof Error ? e.message : String(e),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Save a blob to a file
|
|
248
|
+
*/
|
|
249
|
+
async saveBlob(blob, filename) {
|
|
250
|
+
const finalFilename = filename || `blob-${blob.blobId}.${this.getExtension(blob.mimeType)}`;
|
|
251
|
+
const path = join(this.downloadDir, finalFilename);
|
|
252
|
+
if (blob.content) {
|
|
253
|
+
await fs.writeFile(path, blob.content);
|
|
254
|
+
return path;
|
|
255
|
+
}
|
|
256
|
+
// If content wasn't captured, try to fetch it from the page context
|
|
257
|
+
throw new Error('Blob content not available for saving');
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Get all captured blobs
|
|
261
|
+
*/
|
|
262
|
+
getAllBlobs() {
|
|
263
|
+
return Array.from(this.downloads.values());
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get blob by ID
|
|
267
|
+
*/
|
|
268
|
+
getBlob(blobId) {
|
|
269
|
+
return this.downloads.get(blobId);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get the last blob
|
|
273
|
+
*/
|
|
274
|
+
getLastBlob() {
|
|
275
|
+
const blobs = Array.from(this.downloads.values());
|
|
276
|
+
if (blobs.length === 0)
|
|
277
|
+
return undefined;
|
|
278
|
+
return blobs.sort((a, b) => b.timestamp - a.timestamp)[0];
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get all blobs by MIME type
|
|
282
|
+
*/
|
|
283
|
+
getBlobsByMimeType(mimeType) {
|
|
284
|
+
const blobs = this.getAllBlobs();
|
|
285
|
+
return blobs.filter(blob => {
|
|
286
|
+
if (typeof mimeType === 'string') {
|
|
287
|
+
return blob.mimeType.includes(mimeType);
|
|
288
|
+
}
|
|
289
|
+
return mimeType.test(blob.mimeType);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Clear all captured blobs
|
|
294
|
+
*/
|
|
295
|
+
clearBlobs() {
|
|
296
|
+
this.downloads.clear();
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Get file extension from MIME type
|
|
300
|
+
*/
|
|
301
|
+
getExtension(mimeType) {
|
|
302
|
+
const extensions = {
|
|
303
|
+
'application/pdf': 'pdf',
|
|
304
|
+
'application/json': 'json',
|
|
305
|
+
'application/xml': 'xml',
|
|
306
|
+
'text/xml': 'xml',
|
|
307
|
+
'text/html': 'html',
|
|
308
|
+
'text/plain': 'txt',
|
|
309
|
+
'text/csv': 'csv',
|
|
310
|
+
'image/jpeg': 'jpg',
|
|
311
|
+
'image/png': 'png',
|
|
312
|
+
'image/gif': 'gif',
|
|
313
|
+
'image/webp': 'webp',
|
|
314
|
+
'image/svg+xml': 'svg',
|
|
315
|
+
'video/mp4': 'mp4',
|
|
316
|
+
'video/webm': 'webm',
|
|
317
|
+
'audio/mpeg': 'mp3',
|
|
318
|
+
'audio/wav': 'wav',
|
|
319
|
+
'application/zip': 'zip',
|
|
320
|
+
'application/x-zip-compressed': 'zip',
|
|
321
|
+
};
|
|
322
|
+
for (const [type, ext] of Object.entries(extensions)) {
|
|
323
|
+
if (mimeType.startsWith(type)) {
|
|
324
|
+
return ext;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return 'bin';
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get statistics
|
|
331
|
+
*/
|
|
332
|
+
getStats() {
|
|
333
|
+
return {
|
|
334
|
+
...this.stats,
|
|
335
|
+
averageSize: this.stats.capturedBlobs > 0
|
|
336
|
+
? this.stats.totalSize / this.stats.capturedBlobs
|
|
337
|
+
: 0,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Reset statistics
|
|
342
|
+
*/
|
|
343
|
+
resetStats() {
|
|
344
|
+
this.stats = {
|
|
345
|
+
totalBlobs: 0,
|
|
346
|
+
capturedBlobs: 0,
|
|
347
|
+
totalSize: 0,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get download directory
|
|
352
|
+
*/
|
|
353
|
+
getDownloadDir() {
|
|
354
|
+
return this.downloadDir;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Verify blob content
|
|
358
|
+
*/
|
|
359
|
+
async verifyBlob(blob, verification) {
|
|
360
|
+
const errors = [];
|
|
361
|
+
if (verification.minSize && blob.size < verification.minSize) {
|
|
362
|
+
errors.push(`Size ${blob.size} is below minimum ${verification.minSize}`);
|
|
363
|
+
}
|
|
364
|
+
if (verification.maxSize && blob.size > verification.maxSize) {
|
|
365
|
+
errors.push(`Size ${blob.size} exceeds maximum ${verification.maxSize}`);
|
|
366
|
+
}
|
|
367
|
+
if (verification.contentPattern && blob.content) {
|
|
368
|
+
const content = blob.content.toString('utf-8');
|
|
369
|
+
if (typeof verification.contentPattern === 'string') {
|
|
370
|
+
if (!content.includes(verification.contentPattern)) {
|
|
371
|
+
errors.push(`Content does not include "${verification.contentPattern}"`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
if (!verification.contentPattern.test(content)) {
|
|
376
|
+
errors.push('Content does not match expected pattern');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
valid: errors.length === 0,
|
|
382
|
+
errors,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Factory function to create Blob URL Download Handler
|
|
388
|
+
*/
|
|
389
|
+
export function createBlobURLDownloadHandler(config) {
|
|
390
|
+
return new BlobURLDownloadHandler(config);
|
|
391
|
+
}
|
|
392
|
+
export default BlobURLDownloadHandler;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Consent Handler
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects and dismisses GDPR/CCPA cookie consent banners.
|
|
5
|
+
* Supports all major Consent Management Platforms (CMPs).
|
|
6
|
+
*
|
|
7
|
+
* Philosophy: "80% of EU sites block interaction with consent banners"
|
|
8
|
+
*/
|
|
9
|
+
import type { Page } from '@playwright/test';
|
|
10
|
+
/**
|
|
11
|
+
* Consent handling options
|
|
12
|
+
*/
|
|
13
|
+
export interface ConsentOptions {
|
|
14
|
+
/** Action to take: 'accept', 'reject', or 'ignore' */
|
|
15
|
+
action?: 'accept' | 'reject' | 'ignore';
|
|
16
|
+
/** Timeout for banner detection (ms) */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
/** Custom selector override */
|
|
19
|
+
customSelector?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Consent Handler - Auto-detects and handles GDPR/CCPA banners
|
|
23
|
+
*/
|
|
24
|
+
export declare class ConsentHandler {
|
|
25
|
+
/**
|
|
26
|
+
* Handle consent banners on the page
|
|
27
|
+
*
|
|
28
|
+
* @param page - Playwright Page instance
|
|
29
|
+
* @param options - Consent handling options
|
|
30
|
+
* @returns true if a banner was found and handled, false otherwise
|
|
31
|
+
*/
|
|
32
|
+
static handleConsent(page: Page, options?: ConsentOptions): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Try a specific CMP
|
|
35
|
+
*/
|
|
36
|
+
private static tryCMP;
|
|
37
|
+
/**
|
|
38
|
+
* Handle custom consent selector
|
|
39
|
+
*/
|
|
40
|
+
private static handleCustom;
|
|
41
|
+
/**
|
|
42
|
+
* Get all supported CMP names
|
|
43
|
+
*/
|
|
44
|
+
static getSupportedCMPs(): string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Quick function to handle consent (convenience wrapper)
|
|
48
|
+
*/
|
|
49
|
+
export declare function handleConsent(page: Page, options?: ConsentOptions): Promise<boolean>;
|