quality-intelligence-engine 2.2.3 → 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,67 @@
|
|
|
1
|
+
// src/pipeline/failure-grouping.pipeline.ts
|
|
2
|
+
|
|
3
|
+
import { FailureAnalysisResult } from "../types/failure.types";
|
|
4
|
+
import { saveFailureHistory, loadFailureHistory } from "../history/failure-history.store";
|
|
5
|
+
import { analyzeFailureTrends } from "../history/failure-trend.analyzer";
|
|
6
|
+
|
|
7
|
+
export interface FailureGroupSummary {
|
|
8
|
+
dominantCategory: string;
|
|
9
|
+
groupedCounts: Record<string, number>;
|
|
10
|
+
confidence: number;
|
|
11
|
+
flakyCount: number;
|
|
12
|
+
trend?: ReturnType<typeof analyzeFailureTrends>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function groupFailures(
|
|
16
|
+
results: FailureAnalysisResult[]
|
|
17
|
+
): FailureGroupSummary {
|
|
18
|
+
const groupedCounts: Record<string, number> = {};
|
|
19
|
+
let flakyCount = 0;
|
|
20
|
+
|
|
21
|
+
for (const r of results) {
|
|
22
|
+
const key = r.classification ?? "UNKNOWN";
|
|
23
|
+
groupedCounts[key] = (groupedCounts[key] || 0) + 1;
|
|
24
|
+
|
|
25
|
+
if (key === "FLAKY") {
|
|
26
|
+
flakyCount++;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let dominantCategory = "UNKNOWN";
|
|
31
|
+
let dominantCount = 0;
|
|
32
|
+
|
|
33
|
+
for (const [category, count] of Object.entries(groupedCounts)) {
|
|
34
|
+
if (count > dominantCount) {
|
|
35
|
+
dominantCategory = category;
|
|
36
|
+
dominantCount = count;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const total = results.length || 1;
|
|
41
|
+
let confidence = dominantCount / total;
|
|
42
|
+
|
|
43
|
+
// 🔻 FLAKY PENALTY (KEY LOGIC)
|
|
44
|
+
if (flakyCount > 0) {
|
|
45
|
+
const penalty = Math.min(0.3, flakyCount / total);
|
|
46
|
+
confidence = Math.max(0, confidence - penalty);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const summary: FailureGroupSummary = {
|
|
50
|
+
dominantCategory,
|
|
51
|
+
groupedCounts,
|
|
52
|
+
confidence,
|
|
53
|
+
flakyCount
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
saveFailureHistory({
|
|
57
|
+
runId: Date.now().toString(),
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
dominantCategory,
|
|
60
|
+
groupedCounts,
|
|
61
|
+
confidence
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
summary.trend = analyzeFailureTrends(loadFailureHistory());
|
|
65
|
+
|
|
66
|
+
return summary;
|
|
67
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Reporter,
|
|
3
|
+
TestCase,
|
|
4
|
+
TestResult
|
|
5
|
+
} from "@playwright/test/reporter";
|
|
6
|
+
|
|
7
|
+
import { trackFailure } from "../intelligence/failure-fingerprinting/failure.tracker";
|
|
8
|
+
|
|
9
|
+
export default class QIReporter implements Reporter {
|
|
10
|
+
onTestEnd(test: TestCase, result: TestResult) {
|
|
11
|
+
if (result.status !== "failed") return;
|
|
12
|
+
|
|
13
|
+
const errorText =
|
|
14
|
+
result.error?.message ||
|
|
15
|
+
result.error?.stack ||
|
|
16
|
+
"unknown error";
|
|
17
|
+
|
|
18
|
+
trackFailure(
|
|
19
|
+
{
|
|
20
|
+
error: errorText,
|
|
21
|
+
testName: test.title
|
|
22
|
+
},
|
|
23
|
+
process.env.QI_RUN_ID ?? "local-run"
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/reporter.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AnalysisResult, PassRisk } from './types';
|
|
2
|
+
import { writeAnalysisMarkdown } from './markdownWriter';
|
|
3
|
+
import { RunContext } from './runManager';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* FAILURES: Print summary + write markdown reports
|
|
7
|
+
*/
|
|
8
|
+
export function printSummaryAndWriteReports(
|
|
9
|
+
grouped: Map<string, AnalysisResult[]>,
|
|
10
|
+
run: RunContext,
|
|
11
|
+
config: any
|
|
12
|
+
) {
|
|
13
|
+
let printed = 0;
|
|
14
|
+
|
|
15
|
+
grouped.forEach((items, key) => {
|
|
16
|
+
const sample = items[0];
|
|
17
|
+
|
|
18
|
+
// Respect confidence threshold
|
|
19
|
+
if (
|
|
20
|
+
sample.confidence <
|
|
21
|
+
config.engine.confidenceThresholds.fail
|
|
22
|
+
) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (printed === 0) {
|
|
27
|
+
console.log(
|
|
28
|
+
`❌ Failures Detected (${grouped.size} unique issues)\n`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
printed++;
|
|
33
|
+
|
|
34
|
+
console.log(
|
|
35
|
+
`${printed}) ${sample.category} (${items.length} occurrences)`
|
|
36
|
+
);
|
|
37
|
+
console.log(
|
|
38
|
+
` Location : ${sample.location.file}:${sample.location.line}`
|
|
39
|
+
);
|
|
40
|
+
console.log(
|
|
41
|
+
` API : ${sample.failedApi.endpoint} → ${sample.failedApi.status}`
|
|
42
|
+
);
|
|
43
|
+
console.log(
|
|
44
|
+
` Cause : ${sample.rootCause}`
|
|
45
|
+
);
|
|
46
|
+
console.log(
|
|
47
|
+
` Details : output/${run.runDir.split('/').pop()}/${key}/analysis.md\n`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
writeAnalysisMarkdown(
|
|
51
|
+
run.runDir,
|
|
52
|
+
key,
|
|
53
|
+
sample,
|
|
54
|
+
items.length
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (printed === 0) {
|
|
59
|
+
console.log('✅ No failures exceeded confidence threshold\n');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* PASSES: Print non-blocking risk warnings
|
|
65
|
+
*/
|
|
66
|
+
export function printPassRisks(
|
|
67
|
+
risks: PassRisk[],
|
|
68
|
+
_run: RunContext
|
|
69
|
+
) {
|
|
70
|
+
if (risks.length === 0) return;
|
|
71
|
+
|
|
72
|
+
console.log(`⚠️ Pass Risks Detected (${risks.length})\n`);
|
|
73
|
+
|
|
74
|
+
risks.forEach((risk, i) => {
|
|
75
|
+
console.log(
|
|
76
|
+
`${i + 1}) ${risk.test} → ${risk.riskType}`
|
|
77
|
+
);
|
|
78
|
+
console.log(
|
|
79
|
+
` Location : ${risk.location.file}`
|
|
80
|
+
);
|
|
81
|
+
console.log(
|
|
82
|
+
` Reason : ${risk.reason}`
|
|
83
|
+
);
|
|
84
|
+
console.log(
|
|
85
|
+
` Fix : ${risk.fix}\n`
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class QiReporter {
|
|
5
|
+
onTestEnd(test, result) {
|
|
6
|
+
if (result.status !== 'failed') return;
|
|
7
|
+
|
|
8
|
+
const message = result.error && result.error.message;
|
|
9
|
+
if (!message) return;
|
|
10
|
+
|
|
11
|
+
const isNumericMismatch =
|
|
12
|
+
/Expected:\s*\d+/i.test(message) &&
|
|
13
|
+
/Received:\s*\d+/i.test(message);
|
|
14
|
+
|
|
15
|
+
if (!isNumericMismatch) return;
|
|
16
|
+
|
|
17
|
+
// ✅ Playwright GUARANTEED directory
|
|
18
|
+
const outputDir = result.outputDir;
|
|
19
|
+
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
path.join(outputDir, 'failure-hint.json'),
|
|
22
|
+
JSON.stringify(
|
|
23
|
+
{
|
|
24
|
+
type: 'API_BUG',
|
|
25
|
+
reason: 'Numeric data mismatch inferred from UI assertion'
|
|
26
|
+
},
|
|
27
|
+
null,
|
|
28
|
+
2
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = QiReporter;
|
package/src/rules.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AnalysisResult } from './types';
|
|
2
|
+
|
|
3
|
+
export function generateDummyResult(): AnalysisResult {
|
|
4
|
+
return {
|
|
5
|
+
category: 'BACKEND_API_FAILURE',
|
|
6
|
+
confidence: 0.93,
|
|
7
|
+
location: {
|
|
8
|
+
file: 'AuthController.java',
|
|
9
|
+
line: 87
|
|
10
|
+
},
|
|
11
|
+
failedApi: {
|
|
12
|
+
method: 'GET',
|
|
13
|
+
endpoint: '/users/{id}',
|
|
14
|
+
status: 404,
|
|
15
|
+
response: 'User not found'
|
|
16
|
+
},
|
|
17
|
+
rootCause:
|
|
18
|
+
'The requested user ID does not exist in the database.',
|
|
19
|
+
retryGuidance:
|
|
20
|
+
'Automated CI retry is not advised (high confidence application failure).',
|
|
21
|
+
reproductionSteps: [
|
|
22
|
+
'Start backend service',
|
|
23
|
+
'Run: curl GET /users/{id}',
|
|
24
|
+
'Observe: 404 Not Found'
|
|
25
|
+
],
|
|
26
|
+
expected:
|
|
27
|
+
'User details are returned successfully.',
|
|
28
|
+
actual:
|
|
29
|
+
'API returns 404 and request fails in AuthController.java:87',
|
|
30
|
+
suggestedActions: [
|
|
31
|
+
'Validate user existence before API call',
|
|
32
|
+
'Add graceful handling for missing user records'
|
|
33
|
+
]
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export interface RunContext {
|
|
5
|
+
runId: string;
|
|
6
|
+
runDir: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function createRunContext(): RunContext {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const runId = now
|
|
12
|
+
.toISOString()
|
|
13
|
+
.replace(/[:.]/g, '-')
|
|
14
|
+
.replace('T', '_')
|
|
15
|
+
.replace('Z', '');
|
|
16
|
+
|
|
17
|
+
const runDir = path.join(process.cwd(), 'output', `run-${runId}`);
|
|
18
|
+
fs.mkdirSync(runDir, { recursive: true });
|
|
19
|
+
|
|
20
|
+
return { runId, runDir };
|
|
21
|
+
}
|
package/src/runner.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/runner.ts
|
|
2
|
+
|
|
3
|
+
import { runFailureAnalysisFromPlaywright } from "./pipeline/failure-analysis.pipeline";
|
|
4
|
+
import { groupFailures } from "./pipeline/failure-grouping.pipeline";
|
|
5
|
+
import { enrichWithIntelligence } from "./intelligence/intelligence.pipeline";
|
|
6
|
+
|
|
7
|
+
export async function runQualityIntelligence(input: string) {
|
|
8
|
+
const analysisResults = await runFailureAnalysisFromPlaywright(input);
|
|
9
|
+
const summary = groupFailures(analysisResults);
|
|
10
|
+
const enriched = await enrichWithIntelligence(summary);
|
|
11
|
+
return enriched;
|
|
12
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Page } from '@playwright/test';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
export function attachNetworkCollector(page: Page, testId: string) {
|
|
6
|
+
const failures: any[] = [];
|
|
7
|
+
|
|
8
|
+
page.on('response', response => {
|
|
9
|
+
if (response.status() >= 400) {
|
|
10
|
+
failures.push({
|
|
11
|
+
method: response.request().method(),
|
|
12
|
+
url: new URL(response.url()).pathname,
|
|
13
|
+
status: response.status()
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
page.on('requestfailed', request => {
|
|
19
|
+
failures.push({
|
|
20
|
+
method: request.method(),
|
|
21
|
+
url: new URL(request.url()).pathname,
|
|
22
|
+
error: request.failure()?.errorText
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
process.on('exit', () => {
|
|
27
|
+
if (failures.length === 0) return;
|
|
28
|
+
|
|
29
|
+
const dir = path.join('test-results', testId);
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
fs.writeFileSync(
|
|
32
|
+
path.join(dir, 'network.json'),
|
|
33
|
+
JSON.stringify(failures, null, 2)
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ParsedLocation {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function parseStackTrace(stack: string[]): ParsedLocation {
|
|
7
|
+
if (!stack || stack.length === 0) {
|
|
8
|
+
return { file: 'Unknown', line: -1 };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Supports formats like: Class.method(File.java:87)
|
|
12
|
+
const match = stack[0].match(/\((.*):(\d+)\)/);
|
|
13
|
+
|
|
14
|
+
if (!match) {
|
|
15
|
+
return { file: 'Unknown', line: -1 };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
file: match[1],
|
|
20
|
+
line: Number(match[2])
|
|
21
|
+
};
|
|
22
|
+
}
|
package/src/test-run.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/test-run.ts
|
|
2
|
+
|
|
3
|
+
import { runQualityIntelligence } from "./runner";
|
|
4
|
+
|
|
5
|
+
function line(char = "=", count = 60) {
|
|
6
|
+
console.log(char.repeat(count));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
const playwrightResultsDir = "playwright-results";
|
|
11
|
+
|
|
12
|
+
const result = await runQualityIntelligence(playwrightResultsDir);
|
|
13
|
+
|
|
14
|
+
line();
|
|
15
|
+
console.log("QUALITY INTELLIGENCE – FINAL OUTPUT");
|
|
16
|
+
line();
|
|
17
|
+
|
|
18
|
+
console.log(`Primary Failure : ${result.dominantCategory}`);
|
|
19
|
+
console.log(`Confidence Score : ${result.confidence}`);
|
|
20
|
+
console.log(`Flaky Failures : ${result.flakyCount}`);
|
|
21
|
+
console.log(`Trend Signal : ${result.trend?.signal ?? "N/A"}`);
|
|
22
|
+
|
|
23
|
+
line("-");
|
|
24
|
+
console.log("Failure Breakdown:");
|
|
25
|
+
for (const [k, v] of Object.entries(result.groupedCounts)) {
|
|
26
|
+
console.log(` - ${k}: ${v}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
line("-");
|
|
30
|
+
console.log("Confidence Diagnosis:");
|
|
31
|
+
for (const lineText of result.intelligence.confidence.explanation) {
|
|
32
|
+
console.log(` • ${lineText}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
line("-");
|
|
36
|
+
console.log("AI Explanation:");
|
|
37
|
+
console.log(` Summary: ${result.intelligence.explanation.summary}`);
|
|
38
|
+
|
|
39
|
+
if (result.intelligence.explanation.reasoning?.length) {
|
|
40
|
+
console.log(" Reasoning:");
|
|
41
|
+
for (const r of result.intelligence.explanation.reasoning) {
|
|
42
|
+
console.log(` - ${r}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (result.intelligence.explanation.recommendedAction?.length) {
|
|
47
|
+
console.log(" Recommended Actions:");
|
|
48
|
+
for (const a of result.intelligence.explanation.recommendedAction) {
|
|
49
|
+
console.log(` - ${a}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
line();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main().catch(err => {
|
|
57
|
+
console.error("QUALITY INTELLIGENCE FAILED");
|
|
58
|
+
console.error(err);
|
|
59
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type FailureType = 'TEST_BUG' | 'UI_BUG' | 'API_BUG'
|
|
2
|
+
|
|
3
|
+
export interface AnalysisFailure {
|
|
4
|
+
testName: string
|
|
5
|
+
failureType: FailureType
|
|
6
|
+
confidence: number
|
|
7
|
+
|
|
8
|
+
// Location
|
|
9
|
+
file?: string
|
|
10
|
+
line?: number
|
|
11
|
+
|
|
12
|
+
// Intelligence sections
|
|
13
|
+
rootCause?: string[]
|
|
14
|
+
evidence?: string[]
|
|
15
|
+
diagnosis?: string[]
|
|
16
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/types/failure.types.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared failure contract
|
|
5
|
+
* Used by:
|
|
6
|
+
* - failure-analysis pipeline
|
|
7
|
+
* - grouping / history
|
|
8
|
+
* - CLI reporter
|
|
9
|
+
*
|
|
10
|
+
* Fields are OPTIONAL where produced later in the pipeline.
|
|
11
|
+
*/
|
|
12
|
+
export interface FailureAnalysisResult {
|
|
13
|
+
/** Core classification */
|
|
14
|
+
classification: string;
|
|
15
|
+
|
|
16
|
+
/** Optional human message */
|
|
17
|
+
message?: string;
|
|
18
|
+
|
|
19
|
+
/** CLI / reporting fields */
|
|
20
|
+
testName?: string;
|
|
21
|
+
failureType?: string;
|
|
22
|
+
confidence?: number;
|
|
23
|
+
|
|
24
|
+
/** Diagnosis-style outputs */
|
|
25
|
+
rootCause?: string[];
|
|
26
|
+
evidence?: string[];
|
|
27
|
+
diagnosis?: string[];
|
|
28
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// =====================
|
|
2
|
+
// RAW INPUT TYPES
|
|
3
|
+
// =====================
|
|
4
|
+
|
|
5
|
+
export interface RawApiCall {
|
|
6
|
+
method: string;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
status: number;
|
|
9
|
+
response?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RawFailureInput {
|
|
13
|
+
errorType: 'API_ERROR' | 'TEST_ERROR' | 'ENVIRONMENT_ERROR';
|
|
14
|
+
api?: RawApiCall;
|
|
15
|
+
message: string;
|
|
16
|
+
stackTrace: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RawPassInput {
|
|
20
|
+
testName: string;
|
|
21
|
+
duration: number;
|
|
22
|
+
retries?: number;
|
|
23
|
+
apiCalls?: RawApiCall[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// =====================
|
|
27
|
+
// FAILURE TYPES
|
|
28
|
+
// =====================
|
|
29
|
+
|
|
30
|
+
export type FailureCategory =
|
|
31
|
+
| 'BACKEND_API_FAILURE'
|
|
32
|
+
| 'TEST_FAILURE'
|
|
33
|
+
| 'FLAKY_TEST'
|
|
34
|
+
| 'ENVIRONMENT_ISSUE';
|
|
35
|
+
|
|
36
|
+
export interface FailureLocation {
|
|
37
|
+
file: string;
|
|
38
|
+
line: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FailedApiCall {
|
|
42
|
+
method: string;
|
|
43
|
+
endpoint: string;
|
|
44
|
+
status: number;
|
|
45
|
+
response?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface AnalysisResult {
|
|
49
|
+
category: FailureCategory;
|
|
50
|
+
confidence: number;
|
|
51
|
+
location: FailureLocation;
|
|
52
|
+
failedApi: FailedApiCall;
|
|
53
|
+
rootCause: string;
|
|
54
|
+
retryGuidance: string;
|
|
55
|
+
reproductionSteps: string[];
|
|
56
|
+
expected: string;
|
|
57
|
+
actual: string;
|
|
58
|
+
suggestedActions: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// =====================
|
|
62
|
+
// PASS RISK TYPES
|
|
63
|
+
// =====================
|
|
64
|
+
|
|
65
|
+
export type PassRiskType =
|
|
66
|
+
| 'FLAKY_PASS'
|
|
67
|
+
| 'API_WARNING'
|
|
68
|
+
| 'PERFORMANCE_DRIFT';
|
|
69
|
+
|
|
70
|
+
export interface PassRisk {
|
|
71
|
+
test: string;
|
|
72
|
+
riskType: PassRiskType;
|
|
73
|
+
location: {
|
|
74
|
+
file: string;
|
|
75
|
+
line: number;
|
|
76
|
+
};
|
|
77
|
+
reason: string;
|
|
78
|
+
fix: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// =====================
|
|
82
|
+
// CONFIGURATION TYPES
|
|
83
|
+
// =====================
|
|
84
|
+
|
|
85
|
+
export interface QIConfig {
|
|
86
|
+
engine: {
|
|
87
|
+
mode: 'standard' | 'strict' | 'lenient';
|
|
88
|
+
confidenceThresholds: {
|
|
89
|
+
fail: number; // 0-1
|
|
90
|
+
passRisk: number; // 0-1
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
output: {
|
|
94
|
+
writeMarkdown: boolean;
|
|
95
|
+
maxHistoryRuns: number;
|
|
96
|
+
};
|
|
97
|
+
passAnalysis: {
|
|
98
|
+
enable: boolean;
|
|
99
|
+
reportFlakyPass: boolean;
|
|
100
|
+
reportApiWarnings: boolean;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
type Artifacts = {
|
|
5
|
+
trace?: string
|
|
6
|
+
screenshots: string[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function locatePlaywrightArtifacts(): Artifacts {
|
|
10
|
+
const resultsDir = path.resolve('test-results')
|
|
11
|
+
const screenshots: string[] = []
|
|
12
|
+
let trace: string | undefined
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(resultsDir)) {
|
|
15
|
+
return { screenshots, trace }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const runDirs = fs.readdirSync(resultsDir)
|
|
19
|
+
|
|
20
|
+
for (const dir of runDirs) {
|
|
21
|
+
const fullDir = path.join(resultsDir, dir)
|
|
22
|
+
if (!fs.statSync(fullDir).isDirectory()) continue
|
|
23
|
+
|
|
24
|
+
const files = fs.readdirSync(fullDir)
|
|
25
|
+
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
const fullPath = path.join(fullDir, file)
|
|
28
|
+
|
|
29
|
+
// 🎥 Trace ZIP (take the latest retry if exists)
|
|
30
|
+
if (file === 'trace.zip') {
|
|
31
|
+
trace = fullPath
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 📸 Screenshots
|
|
35
|
+
if (file.endsWith('.png')) {
|
|
36
|
+
screenshots.push(fullPath)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { trace, screenshots }
|
|
42
|
+
}
|