quality-intelligence-engine 2.2.2 → 2.2.4
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/.github/workflows/playwright.yml +27 -0
- package/.github/workflows/quality-intelligence.yml +45 -0
- package/CHANGELOG.md +164 -0
- package/REFACTORING_SUMMARY.md +417 -0
- package/artifacts/failure-analysis/run-1769595909824-login_with_valid_user.md +25 -0
- package/artifacts/failure-analysis/run-1769596445203-login_with_valid_user.md +25 -0
- package/artifacts/failure-analysis/run-1769596573162-login_with_valid_user.md +34 -0
- package/artifacts/failure-analysis/run-1769596591727-login_with_valid_user.md +25 -0
- package/artifacts/failure-analysis/run-1769596600117-login_with_valid_user.md +34 -0
- package/artifacts/failure-analysis/run-1769596782107-login_with_valid_user.md +32 -0
- package/artifacts/failure-analysis/run-1769596940770-login_with_valid_user.md +28 -0
- package/artifacts/failure-analysis/run-1769596960417-login_with_valid_user.md +28 -0
- package/artifacts/failure-analysis/run-1769596981303-login_with_valid_user.md +28 -0
- package/artifacts/failure-analysis/run-1769597351831-login_with_valid_user.md +21 -0
- package/artifacts/failure-analysis/run-1769597486816-login_with_valid_user.md +21 -0
- package/artifacts/failure-analysis/run-1769599708378-login_with_valid_user.md +22 -0
- package/artifacts/failure-analysis/run-1769600327960-login_with_valid_user.md +22 -0
- package/artifacts/failure-analysis/run-1769600596201-saucedemo__login_fails_with_wrong_password_fail.md +22 -0
- package/artifacts/failure-analysis/run-1769600682675-saucedemo__login_fails_with_wrong_password_fail.md +22 -0
- package/artifacts/failure-analysis/run-1769602090701-saucedemo__add_to_cart_button_is_hidden_fail.md +30 -0
- package/artifacts/failure-analysis/run-1769602090701-saucedemo__cart_count_updates_to_2_after_adding_one_item_fail.md +30 -0
- package/artifacts/failure-analysis/run-1769602090701-saucedemo__checkout_navigates_to_payment_page_directly_fail.md +30 -0
- package/artifacts/failure-analysis/run-1769602090701-saucedemo__inventory_shows_10_products_fail.md +30 -0
- package/artifacts/failure-analysis/run-1769602090701-saucedemo__products_sorted_high_to_low_start_with_999_fail.md +30 -0
- package/artifacts/failure-history/1769696674298.json +9 -0
- package/artifacts/failure-history/1769697768622.json +10 -0
- package/artifacts/failure-history/1769697923557.json +10 -0
- package/artifacts/failure-history/1769698160213.json +11 -0
- package/artifacts/failure-history/1769698353440.json +11 -0
- package/artifacts/failure-history/1769698763306.json +11 -0
- package/artifacts/failure-history/1769698878947.json +11 -0
- package/artifacts/failure-history/1769699909817.json +11 -0
- package/artifacts/failure-history/1769703130913.json +11 -0
- package/artifacts/failure-history/1769703142113.json +11 -0
- package/artifacts/failure-history/1769703158978.json +11 -0
- package/artifacts/failure-history/1769703406025.json +11 -0
- package/artifacts/failure-history/1769703422720.json +11 -0
- package/artifacts/failure-history/1769703518837.json +11 -0
- package/artifacts/failure-history/1769703530419.json +11 -0
- package/artifacts/failure-history/1769703577078.json +11 -0
- package/artifacts/failure-history/1769704067098.json +11 -0
- package/artifacts/failure-history/1769704074618.json +11 -0
- package/artifacts/failure-history/1769704084948.json +11 -0
- package/artifacts/failure-history/1769704091950.json +11 -0
- package/artifacts/failure-history/1769704435355.json +11 -0
- package/artifacts/failure-history/1769704832871.json +11 -0
- package/artifacts/failure-history/1769707051830.json +11 -0
- package/artifacts/failure-history/1769739820395.json +11 -0
- package/artifacts/failure-history/1769739820400.json +11 -0
- package/artifacts/failure-history/1769742422254.json +11 -0
- package/artifacts/failure-history/1769743106016.json +11 -0
- package/artifacts/failure-history/1769743121857.json +11 -0
- package/artifacts/failure-history/1769750875212.json +11 -0
- package/artifacts/failure-history/1769750880790.json +11 -0
- package/artifacts/failure-history/1769761177923.json +11 -0
- package/artifacts/failure-history/1769761191176.json +11 -0
- package/bin/qi.ts +37 -0
- package/config/agent.config.json +18 -0
- package/data/failures/fingerprints/0ded7b45.json +10 -0
- package/data/failures/fingerprints/2437666a.json +8 -0
- package/data/failures/fingerprints/3746e3b4.json +42 -0
- package/data/failures/fingerprints/533e258f.json +10 -0
- package/data/failures/fingerprints/56b547d3.json +10 -0
- package/data/failures/fingerprints/693661fa.json +10 -0
- package/data/failures/fingerprints/789126b1.json +8 -0
- package/data/failures/fingerprints/936d995e.json +10 -0
- package/data/failures/fingerprints/ba5f6c0a.json +10 -0
- package/data/failures/fingerprints/f148a261.json +56 -0
- package/data/failures/fingerprints/fa14440f.json +14 -0
- package/data/failures/registry.json +57 -0
- package/data/flakiness/history.json +71 -0
- package/dist/bin/qi.js +0 -0
- package/dist/src/intelligence/failure-fingerprinting/failure.classifier.js +5 -1
- package/dist/src/intelligence/failure-fingerprinting/failure.tracker.js +5 -0
- package/dist/src/playwright/qi.reporter.js +17 -0
- package/final.sanity.test.ts +46 -0
- package/input/raw-failure.json +36 -0
- package/input/raw-pass.json +33 -0
- package/output/run-2026-01-28_02-48-38-420/BACKEND_API_FAILURE-AuthController.java-/users/{id}/analysis.md +50 -0
- package/output/run-2026-01-28_02-48-38-420/TEST_FAILURE-Unknown--/analysis.md +50 -0
- package/output/run-2026-01-28_02-48-52-140/BACKEND_API_FAILURE-AuthController.java-/users/{id}/analysis.md +50 -0
- package/output/run-2026-01-28_02-48-52-140/TEST_FAILURE-Unknown--/analysis.md +50 -0
- package/output/run-2026-01-28_03-13-16-302/BACKEND_API_FAILURE-AuthController.java-/users/{id}/analysis.md +50 -0
- package/output/run-2026-01-28_03-13-16-302/PASS-checkout.spec.ts-API_WARNING/analysis.md +49 -0
- package/output/run-2026-01-28_03-13-16-302/PASS-login.spec.ts-FLAKY_PASS/analysis.md +49 -0
- package/output/run-2026-01-28_03-13-16-302/TEST_FAILURE-Unknown--/analysis.md +50 -0
- package/output/run-2026-01-28_03-29-49-786/BACKEND_API_FAILURE-AuthController.java-/users/{id}/analysis.md +50 -0
- package/package.json +4 -10
- package/playwright-results/.last-run.json +17 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium/video.webm +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--chromium-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox/video.webm +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--firefox-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit/video.webm +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-email-like-valid-d5dc6-rname-format-rejected-FAIL--webkit-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--chromium-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--firefox-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-7c4f3-to-cart-button-hidden-FAIL--webkit-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--chromium-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--firefox-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit-retry1/error-context.md +112 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-cd423-entory-count-mismatch-FAIL--webkit-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--chromium-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--firefox-retry1/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit/video.webm +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit-retry1/error-context.md +31 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit-retry1/test-failed-1.png +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit-retry1/trace.zip +0 -0
- package/playwright-results/saucedemo-numeric-mismatch-f085e-E-unauthorized-access-FAIL--webkit-retry1/video.webm +0 -0
- package/playwright.config.ts +42 -0
- package/quality-intelligence-engine-2.2.0.tgz +0 -0
- package/quality-intelligence-engine-2.2.2.tgz +0 -0
- package/sanity.test.ts +33 -0
- package/src/adapters/playwright-folder.adapter.ts +85 -0
- package/src/adapters/playwright-json.adapter.ts +49 -0
- package/src/adapters/playwright.adapter.ts +44 -0
- package/src/cli/qi-test.ts +20 -0
- package/src/cli/qi.ts +67 -0
- package/src/cli/ui/divider.ts +9 -0
- package/src/cli/ui/failureLogger.ts +33 -0
- package/src/configLoader.ts +70 -0
- package/src/console/issue-view.ts +193 -0
- package/src/failure-analysis/failure-analyzer.ts +43 -0
- package/src/final-run.ts +174 -0
- package/src/final-runner.ts +31 -0
- package/src/fixtures/networkCollector.ts +27 -0
- package/src/history/failure-history.store.ts +23 -0
- package/src/history/failure-history.types.ts +12 -0
- package/src/history/failure-trend.analyzer.ts +49 -0
- package/src/index.ts +10 -0
- package/src/integrations/ci/ci-annotator.ts +42 -0
- package/src/intelligence/confidence-calibration.engine.ts +41 -0
- package/src/intelligence/decision-intelligence/confidence.engine.ts +18 -0
- package/src/intelligence/decision-intelligence/decision.config.ts +18 -0
- package/src/intelligence/decision-intelligence/decision.engine.ts +30 -0
- package/src/intelligence/decision-intelligence/decision.types.ts +15 -0
- package/src/intelligence/failure-fingerprinting/failure.classifier.ts +61 -0
- package/src/intelligence/failure-fingerprinting/failure.store.ts +42 -0
- package/src/intelligence/failure-fingerprinting/failure.tracker.ts +28 -0
- package/src/intelligence/failure-fingerprinting/fingerprint.generator.ts +28 -0
- package/src/intelligence/failure-fingerprinting/fingerprint.types.ts +14 -0
- package/src/intelligence/flakiness-intelligence/flakiness.analyzer.ts +23 -0
- package/src/intelligence/flakiness-intelligence/flakiness.fingerprint.ts +14 -0
- package/src/intelligence/flakiness-intelligence/flakiness.store.ts +24 -0
- package/src/intelligence/flakiness-intelligence/flakiness.tracker.ts +42 -0
- package/src/intelligence/flakiness-intelligence/flakiness.types.ts +8 -0
- package/src/intelligence/intelligence.pipeline.ts +20 -0
- package/src/intelligence/llm-explainer.ts +29 -0
- package/src/intelligence/root-cause/rootcause.engine.ts +46 -0
- package/src/intelligence/trend-intelligence/trend.engine.ts +42 -0
- package/src/markdownWriter.ts +68 -0
- package/src/normalizer.ts +31 -0
- package/src/passAnalyzer.ts +38 -0
- package/src/pipeline/ai.summarizer.ts +39 -0
- package/src/pipeline/failure-analysis.pipeline.ts +13 -0
- package/src/pipeline/failure-grouping.pipeline.ts +67 -0
- package/src/playwright/qi.reporter.ts +26 -0
- package/src/reporter.ts +88 -0
- package/src/reporters/qi-reporter.js +34 -0
- package/src/rules.ts +35 -0
- package/src/runManager.ts +21 -0
- package/src/runner.ts +12 -0
- package/src/runtime/networkCollector.ts +36 -0
- package/src/stackParser.ts +22 -0
- package/src/test-run.ts +59 -0
- package/src/types/analysis-result.ts +16 -0
- package/src/types/failure.types.ts +28 -0
- package/src/types/playwright-failure.ts +10 -0
- package/src/types.ts +102 -0
- package/src/utils/artifact.locator.ts +42 -0
- package/src/utils/confidence-constants.ts +111 -0
- package/src/utils/file-utils.ts +146 -0
- package/src/v2/llm/llm-advisor.ts +22 -0
- package/src/v2/pipeline/v2-intelligence.pipeline.ts +21 -0
- package/src/v2/self-healing/self-healer.ts +60 -0
- package/src/v2/trace-intelligence/trace-analyzer.ts +42 -0
- package/src/v2-test-run.ts +74 -0
- package/tests/fixtures.ts +40 -0
- package/tests/saucedemo-login-validation.spec.ts +74 -0
- package/tsconfig.json +16 -0
- package/tsconfig.playwright.json +13 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confidence Calculation Constants
|
|
3
|
+
*
|
|
4
|
+
* These constants define the base confidence levels and adjustments
|
|
5
|
+
* used throughout the quality intelligence engine.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Base confidence levels for different error types
|
|
10
|
+
*/
|
|
11
|
+
export const BASE_CONFIDENCE = {
|
|
12
|
+
/** Strong backend failure signal (5xx errors) */
|
|
13
|
+
BACKEND_SERVER_ERROR: 0.95,
|
|
14
|
+
|
|
15
|
+
/** Backend API failure (any API error) */
|
|
16
|
+
BACKEND_API_ERROR: 0.9,
|
|
17
|
+
|
|
18
|
+
/** Test or automation failure */
|
|
19
|
+
TEST_ERROR: 0.75,
|
|
20
|
+
|
|
21
|
+
/** Unknown or weak signal */
|
|
22
|
+
UNKNOWN: 0.6,
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Confidence adjustments based on test context and signals
|
|
27
|
+
*/
|
|
28
|
+
export const CONFIDENCE_ADJUSTMENTS = {
|
|
29
|
+
/** UI successfully rendered and reached assertion */
|
|
30
|
+
UI_RENDERED: 0.05,
|
|
31
|
+
|
|
32
|
+
/** Test involves authentication or login flow */
|
|
33
|
+
AUTH_FLOW_DETECTED: 0.05,
|
|
34
|
+
|
|
35
|
+
/** Explicit authentication failure detected */
|
|
36
|
+
EXPLICIT_AUTH_FAILURE: 0.1,
|
|
37
|
+
|
|
38
|
+
/** Test involves inventory or data operations */
|
|
39
|
+
DATA_OPERATION_DETECTED: 0.05,
|
|
40
|
+
|
|
41
|
+
/** Numeric mismatch detected in assertions */
|
|
42
|
+
NUMERIC_MISMATCH: 0.05,
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Maximum and minimum confidence bounds
|
|
47
|
+
*/
|
|
48
|
+
export const CONFIDENCE_BOUNDS = {
|
|
49
|
+
/** Maximum confidence score (never 100% certain) */
|
|
50
|
+
MAX: 0.95,
|
|
51
|
+
|
|
52
|
+
/** Minimum confidence score for reporting */
|
|
53
|
+
MIN: 0.5,
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Confidence thresholds for decision making
|
|
58
|
+
*/
|
|
59
|
+
export const CONFIDENCE_THRESHOLDS = {
|
|
60
|
+
/** Threshold for blocking CI/CD (high confidence failure) */
|
|
61
|
+
FAIL: 0.85,
|
|
62
|
+
|
|
63
|
+
/** Threshold for reporting pass risks */
|
|
64
|
+
PASS_RISK: 0.6,
|
|
65
|
+
|
|
66
|
+
/** Threshold for investigating (medium confidence) */
|
|
67
|
+
INVESTIGATE: 0.7,
|
|
68
|
+
|
|
69
|
+
/** Threshold for shipping (high confidence in safety) */
|
|
70
|
+
SHIP: 0.85,
|
|
71
|
+
} as const;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Taxonomy rule base confidence levels
|
|
75
|
+
*/
|
|
76
|
+
export const TAXONOMY_BASE_CONFIDENCE = {
|
|
77
|
+
/** Numeric data mismatch */
|
|
78
|
+
NUMERIC_MISMATCH: 0.6,
|
|
79
|
+
|
|
80
|
+
/** Authentication failure */
|
|
81
|
+
AUTH_FAILURE: 0.7,
|
|
82
|
+
|
|
83
|
+
/** UI visibility issue */
|
|
84
|
+
VISIBILITY: 0.7,
|
|
85
|
+
|
|
86
|
+
/** Network timeout */
|
|
87
|
+
TIMEOUT: 0.65,
|
|
88
|
+
|
|
89
|
+
/** Element not found */
|
|
90
|
+
ELEMENT_NOT_FOUND: 0.7,
|
|
91
|
+
|
|
92
|
+
/** Default fallback */
|
|
93
|
+
DEFAULT: 0.7,
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Helper function to clamp confidence within bounds
|
|
98
|
+
*/
|
|
99
|
+
export function clampConfidence(value: number): number {
|
|
100
|
+
return Math.max(
|
|
101
|
+
CONFIDENCE_BOUNDS.MIN,
|
|
102
|
+
Math.min(CONFIDENCE_BOUNDS.MAX, value)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Helper function to format confidence as percentage string
|
|
108
|
+
*/
|
|
109
|
+
export function formatConfidence(value: number): string {
|
|
110
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
111
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Custom error class for file operations
|
|
6
|
+
*/
|
|
7
|
+
export class FileOperationError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
message: string,
|
|
10
|
+
public readonly filePath: string,
|
|
11
|
+
public readonly operation: 'read' | 'write' | 'parse',
|
|
12
|
+
public readonly originalError?: Error
|
|
13
|
+
) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'FileOperationError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Safely read and parse a JSON file with proper error handling
|
|
21
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
22
|
+
* @returns Parsed JSON object
|
|
23
|
+
* @throws FileOperationError if file cannot be read or parsed
|
|
24
|
+
*/
|
|
25
|
+
export function readJsonFile<T>(filePath: string): T {
|
|
26
|
+
try {
|
|
27
|
+
// Check if file exists
|
|
28
|
+
if (!fs.existsSync(filePath)) {
|
|
29
|
+
throw new FileOperationError(
|
|
30
|
+
`File not found: ${filePath}`,
|
|
31
|
+
filePath,
|
|
32
|
+
'read'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Read file content
|
|
37
|
+
let content: string;
|
|
38
|
+
try {
|
|
39
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new FileOperationError(
|
|
42
|
+
`Failed to read file: ${filePath}`,
|
|
43
|
+
filePath,
|
|
44
|
+
'read',
|
|
45
|
+
error as Error
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parse JSON
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(content) as T;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new FileOperationError(
|
|
54
|
+
`Invalid JSON in file: ${filePath}`,
|
|
55
|
+
filePath,
|
|
56
|
+
'parse',
|
|
57
|
+
error as Error
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error instanceof FileOperationError) {
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
throw new FileOperationError(
|
|
65
|
+
`Unexpected error reading ${filePath}: ${(error as Error).message}`,
|
|
66
|
+
filePath,
|
|
67
|
+
'read',
|
|
68
|
+
error as Error
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Safely write JSON to a file with proper error handling
|
|
75
|
+
* @param filePath - Absolute or relative path to the JSON file
|
|
76
|
+
* @param data - Data to write (will be JSON.stringified)
|
|
77
|
+
* @param pretty - Whether to pretty-print the JSON (default: true)
|
|
78
|
+
*/
|
|
79
|
+
export function writeJsonFile<T>(
|
|
80
|
+
filePath: string,
|
|
81
|
+
data: T,
|
|
82
|
+
pretty: boolean = true
|
|
83
|
+
): void {
|
|
84
|
+
try {
|
|
85
|
+
// Ensure directory exists
|
|
86
|
+
const dir = path.dirname(filePath);
|
|
87
|
+
if (!fs.existsSync(dir)) {
|
|
88
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Stringify data
|
|
92
|
+
const content = pretty
|
|
93
|
+
? JSON.stringify(data, null, 2)
|
|
94
|
+
: JSON.stringify(data);
|
|
95
|
+
|
|
96
|
+
// Write to file
|
|
97
|
+
try {
|
|
98
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new FileOperationError(
|
|
101
|
+
`Failed to write file: ${filePath}`,
|
|
102
|
+
filePath,
|
|
103
|
+
'write',
|
|
104
|
+
error as Error
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof FileOperationError) {
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
throw new FileOperationError(
|
|
112
|
+
`Unexpected error writing ${filePath}: ${(error as Error).message}`,
|
|
113
|
+
filePath,
|
|
114
|
+
'write',
|
|
115
|
+
error as Error
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Check if a file exists
|
|
122
|
+
* @param filePath - Path to check
|
|
123
|
+
* @returns true if file exists
|
|
124
|
+
*/
|
|
125
|
+
export function fileExists(filePath: string): boolean {
|
|
126
|
+
return fs.existsSync(filePath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Ensure a directory exists, creating it if necessary
|
|
131
|
+
* @param dirPath - Directory path to ensure
|
|
132
|
+
*/
|
|
133
|
+
export function ensureDirectory(dirPath: string): void {
|
|
134
|
+
if (!fs.existsSync(dirPath)) {
|
|
135
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Resolve a path relative to the project root
|
|
141
|
+
* @param relativePath - Path relative to project root
|
|
142
|
+
* @returns Absolute path
|
|
143
|
+
*/
|
|
144
|
+
export function resolvePath(...segments: string[]): string {
|
|
145
|
+
return path.join(process.cwd(), ...segments);
|
|
146
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/v2/llm/llm-advisor.ts
|
|
2
|
+
|
|
3
|
+
export interface LLMAdvice {
|
|
4
|
+
summary: string;
|
|
5
|
+
fixRecommendation: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Stub for ChatGPT / Azure / Local LLM
|
|
10
|
+
*/
|
|
11
|
+
export async function getLLMAdvice(
|
|
12
|
+
failureSummary: string
|
|
13
|
+
): Promise<LLMAdvice> {
|
|
14
|
+
return {
|
|
15
|
+
summary: `AI analysis of failure: ${failureSummary}`,
|
|
16
|
+
fixRecommendation: [
|
|
17
|
+
"Check backend API response",
|
|
18
|
+
"Validate UI rendering timing",
|
|
19
|
+
"Review recent code changes"
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/v2/pipeline/v2-intelligence.pipeline.ts
|
|
2
|
+
|
|
3
|
+
import { analyzeTrace } from "../trace-intelligence/trace-analyzer";
|
|
4
|
+
import { attemptSelfHeal } from "../self-healing/self-healer";
|
|
5
|
+
import { getLLMAdvice } from "../llm/llm-advisor";
|
|
6
|
+
|
|
7
|
+
export async function runV2Intelligence(
|
|
8
|
+
failureFolder: string,
|
|
9
|
+
failureType: string,
|
|
10
|
+
errorMessage: string
|
|
11
|
+
) {
|
|
12
|
+
const trace = analyzeTrace(failureFolder);
|
|
13
|
+
const healing = attemptSelfHeal(failureType, errorMessage);
|
|
14
|
+
const llm = await getLLMAdvice(failureType);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
trace,
|
|
18
|
+
healing,
|
|
19
|
+
llm
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/v2/self-healing/self-healer.ts
|
|
2
|
+
|
|
3
|
+
export interface HealingSuggestion {
|
|
4
|
+
reason: string;
|
|
5
|
+
suggestedFix: string;
|
|
6
|
+
confidence: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Self-Healing Decision Engine (V2)
|
|
11
|
+
* Decides WHAT can be auto-fixed and WHAT should not
|
|
12
|
+
*/
|
|
13
|
+
export function attemptSelfHeal(
|
|
14
|
+
failureType: string,
|
|
15
|
+
errorMessage: string
|
|
16
|
+
): HealingSuggestion | null {
|
|
17
|
+
|
|
18
|
+
const msg = errorMessage.toLowerCase();
|
|
19
|
+
|
|
20
|
+
// 🔁 Flaky tests
|
|
21
|
+
if (failureType === "FLAKY") {
|
|
22
|
+
return {
|
|
23
|
+
reason: "Intermittent retry-based failure detected",
|
|
24
|
+
suggestedFix: "Add proper waits or retry assertions",
|
|
25
|
+
confidence: 0.6
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 🧭 UI / Locator issues
|
|
30
|
+
if (failureType === "UI_STATE" || failureType === "LOCATOR_CHANGED") {
|
|
31
|
+
return {
|
|
32
|
+
reason: "UI element state or locator changed",
|
|
33
|
+
suggestedFix: "Re-locate element using role/text-based selectors",
|
|
34
|
+
confidence: 0.7
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 🔢 Numeric mismatch — CONDITIONAL healing
|
|
39
|
+
if (failureType === "NUMERIC_MISMATCH") {
|
|
40
|
+
|
|
41
|
+
// UI-level numeric problems (healable)
|
|
42
|
+
if (
|
|
43
|
+
msg.includes("count") ||
|
|
44
|
+
msg.includes("items") ||
|
|
45
|
+
msg.includes("length") ||
|
|
46
|
+
msg.includes("round")
|
|
47
|
+
) {
|
|
48
|
+
return {
|
|
49
|
+
reason: "UI count or rounding issue detected",
|
|
50
|
+
suggestedFix: "Update assertion logic or normalize UI values",
|
|
51
|
+
confidence: 0.55
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Backend/API numeric issues (DO NOT HEAL)
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/v2/trace-intelligence/trace-analyzer.ts
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
export interface TraceInsight {
|
|
7
|
+
page: string;
|
|
8
|
+
failureStep: string;
|
|
9
|
+
screenshots: string[];
|
|
10
|
+
traceFile?: string;
|
|
11
|
+
note?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function analyzeTrace(failureFolder: string): TraceInsight {
|
|
15
|
+
const fullPath = path.resolve(failureFolder);
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(fullPath)) {
|
|
18
|
+
return {
|
|
19
|
+
page: "UNKNOWN",
|
|
20
|
+
failureStep: "Trace folder not found",
|
|
21
|
+
screenshots: [],
|
|
22
|
+
note: `Missing folder: ${failureFolder}`
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const files = fs.readdirSync(fullPath);
|
|
27
|
+
|
|
28
|
+
const screenshots = files
|
|
29
|
+
.filter(f => f.endsWith(".png") || f.endsWith(".jpg"))
|
|
30
|
+
.map(f => path.join(fullPath, f));
|
|
31
|
+
|
|
32
|
+
const traceFile = files.includes("trace.zip")
|
|
33
|
+
? path.join(fullPath, "trace.zip")
|
|
34
|
+
: undefined;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
page: "UNKNOWN_PAGE",
|
|
38
|
+
failureStep: "Action before failure",
|
|
39
|
+
screenshots,
|
|
40
|
+
traceFile
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/v2-test-run.ts
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { runV2Intelligence } from "./v2/pipeline/v2-intelligence.pipeline";
|
|
6
|
+
|
|
7
|
+
function pickOneFailureFolder(): string | null {
|
|
8
|
+
const base = "playwright-results";
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(base)) return null;
|
|
11
|
+
|
|
12
|
+
const entries = fs.readdirSync(base, { withFileTypes: true });
|
|
13
|
+
const folder = entries.find(e => e.isDirectory());
|
|
14
|
+
|
|
15
|
+
return folder ? path.join(base, folder.name) : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function run() {
|
|
19
|
+
const failureFolder = pickOneFailureFolder();
|
|
20
|
+
|
|
21
|
+
if (!failureFolder) {
|
|
22
|
+
console.error("No Playwright failure folders found.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = await runV2Intelligence(
|
|
27
|
+
failureFolder,
|
|
28
|
+
"NUMERIC_MISMATCH",
|
|
29
|
+
"Expected 10, received 6"
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
console.log("==================================================");
|
|
33
|
+
console.log("V2 TRACE INTELLIGENCE OUTPUT");
|
|
34
|
+
console.log("==================================================");
|
|
35
|
+
|
|
36
|
+
console.log(`Failure Folder : ${failureFolder}`);
|
|
37
|
+
console.log(`Failure Type : NUMERIC_MISMATCH`);
|
|
38
|
+
|
|
39
|
+
console.log("\nEvidence:");
|
|
40
|
+
if (result.trace.screenshots.length > 0) {
|
|
41
|
+
console.log(" Screenshots:");
|
|
42
|
+
result.trace.screenshots.forEach(s =>
|
|
43
|
+
console.log(` - ${s}`)
|
|
44
|
+
);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(" Screenshots: none");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (result.trace.traceFile) {
|
|
50
|
+
console.log(` Trace File : ${result.trace.traceFile}`);
|
|
51
|
+
console.log(` Open with : npx playwright show-trace ${result.trace.traceFile}`);
|
|
52
|
+
} else {
|
|
53
|
+
console.log(" Trace File : none");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log("\nSelf-Healing:");
|
|
57
|
+
if (result.healing) {
|
|
58
|
+
console.log(` Reason : ${result.healing.reason}`);
|
|
59
|
+
console.log(` SuggestedFix : ${result.healing.suggestedFix}`);
|
|
60
|
+
console.log(` Confidence : ${result.healing.confidence}`);
|
|
61
|
+
} else {
|
|
62
|
+
console.log(" Not safe to auto-heal");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log("\nAI Advice:");
|
|
66
|
+
console.log(` Summary: ${result.llm.summary}`);
|
|
67
|
+
result.llm.fixRecommendation.forEach(r =>
|
|
68
|
+
console.log(` - ${r}`)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
console.log("==================================================");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
run();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { test as base, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
type FailureHint = {
|
|
4
|
+
type: 'API_BUG' | 'UI_BUG';
|
|
5
|
+
reason: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const test = base.extend({
|
|
9
|
+
page: async ({ page }, use, testInfo) => {
|
|
10
|
+
try {
|
|
11
|
+
await use(page);
|
|
12
|
+
} catch (error: any) {
|
|
13
|
+
const message = error?.message ?? '';
|
|
14
|
+
|
|
15
|
+
let hint: FailureHint | null = null;
|
|
16
|
+
|
|
17
|
+
// ✅ Numeric assertion mismatch → API_BUG
|
|
18
|
+
if (
|
|
19
|
+
/Expected:\s*\d+/i.test(message) &&
|
|
20
|
+
/Received:\s*\d+/i.test(message)
|
|
21
|
+
) {
|
|
22
|
+
hint = {
|
|
23
|
+
type: 'API_BUG',
|
|
24
|
+
reason: 'Numeric data mismatch inferred from UI assertion'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (hint) {
|
|
29
|
+
await testInfo.attach('failure-hint', {
|
|
30
|
+
body: JSON.stringify(hint, null, 2),
|
|
31
|
+
contentType: 'application/json'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export { expect };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// tests/saucedemo-login-validation.spec.ts
|
|
2
|
+
import { test, expect } from '@playwright/test';
|
|
3
|
+
|
|
4
|
+
const URL = 'https://www.saucedemo.com/';
|
|
5
|
+
const USERNAME = '#user-name';
|
|
6
|
+
const PASSWORD = '#password';
|
|
7
|
+
const LOGIN_BTN = '#login-button';
|
|
8
|
+
const ERROR = '[data-test="error"]';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 1️⃣ VALID LOGIN
|
|
12
|
+
*/
|
|
13
|
+
test('Login | VALID | standard_user should login successfully', async ({ page }) => {
|
|
14
|
+
await page.goto(URL);
|
|
15
|
+
|
|
16
|
+
await page.fill(USERNAME, 'standard_user');
|
|
17
|
+
await page.fill(PASSWORD, 'secret_sauce');
|
|
18
|
+
await page.click(LOGIN_BTN);
|
|
19
|
+
|
|
20
|
+
await expect(page).toHaveURL(/inventory.html/);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 2️⃣ INVALID USERNAME
|
|
25
|
+
*/
|
|
26
|
+
test('Login | INVALID | wrong username should fail', async ({ page }) => {
|
|
27
|
+
await page.goto(URL);
|
|
28
|
+
|
|
29
|
+
await page.fill(USERNAME, 'invalid_user');
|
|
30
|
+
await page.fill(PASSWORD, 'secret_sauce');
|
|
31
|
+
await page.click(LOGIN_BTN);
|
|
32
|
+
|
|
33
|
+
await expect(page.locator(ERROR)).toBeVisible();
|
|
34
|
+
await expect(page.locator(ERROR)).toContainText(/do not match/i);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 3️⃣ LOCKED OUT USER
|
|
39
|
+
*/
|
|
40
|
+
test('Login | LOCKED | locked_out_user should be blocked', async ({ page }) => {
|
|
41
|
+
await page.goto(URL);
|
|
42
|
+
|
|
43
|
+
await page.fill(USERNAME, 'locked_out_user');
|
|
44
|
+
await page.fill(PASSWORD, 'secret_sauce');
|
|
45
|
+
await page.click(LOGIN_BTN);
|
|
46
|
+
|
|
47
|
+
await expect(page.locator(ERROR)).toBeVisible();
|
|
48
|
+
await expect(page.locator(ERROR)).toContainText(/locked out/i);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 4️⃣ EMPTY CREDENTIALS
|
|
53
|
+
*/
|
|
54
|
+
test('Login | EMPTY | username and password required', async ({ page }) => {
|
|
55
|
+
await page.goto(URL);
|
|
56
|
+
|
|
57
|
+
await page.click(LOGIN_BTN);
|
|
58
|
+
|
|
59
|
+
await expect(page.locator(ERROR)).toBeVisible();
|
|
60
|
+
await expect(page.locator(ERROR)).toContainText(/Username is required/i);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 5️⃣ SQL-LIKE INPUT (SECURITY SANITY)
|
|
65
|
+
*/
|
|
66
|
+
test('Login | SECURITY | SQL-like input should be rejected', async ({ page }) => {
|
|
67
|
+
await page.goto(URL);
|
|
68
|
+
|
|
69
|
+
await page.fill(USERNAME, `' OR 1=1 --`);
|
|
70
|
+
await page.fill(PASSWORD, `' OR 1=1 --`);
|
|
71
|
+
await page.click(LOGIN_BTN);
|
|
72
|
+
|
|
73
|
+
await expect(page.locator(ERROR)).toBeVisible();
|
|
74
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"outDir": "dist",
|
|
4
|
+
"rootDir": ".",
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"target": "ES2022",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*",
|
|
14
|
+
"bin/**/*"
|
|
15
|
+
]
|
|
16
|
+
}
|