@applitools/eyes-playwright 1.40.7 → 1.41.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/dist/fixture/report-plugin/core/log.js +44 -0
  3. package/dist/fixture/report-plugin/core/types.js +5 -0
  4. package/dist/fixture/report-plugin/data/dataParser.js +126 -0
  5. package/dist/fixture/report-plugin/data/uiUtils.js +10 -0
  6. package/dist/fixture/report-plugin/data/urlManager.js +53 -0
  7. package/dist/fixture/report-plugin/handlers/domChangesHandler.js +58 -0
  8. package/dist/fixture/report-plugin/handlers/statusUpdateHandler.js +52 -0
  9. package/dist/fixture/report-plugin/handlers/urlChangeHandler.js +52 -0
  10. package/dist/fixture/report-plugin/management/playwrightStatusUpdater.js +73 -0
  11. package/dist/fixture/report-plugin/management/pollingManager.js +235 -0
  12. package/dist/fixture/report-plugin/management/reportDataManager.js +32 -0
  13. package/dist/fixture/report-plugin/management/statusUtils.js +102 -0
  14. package/dist/fixture/report-plugin/reportRenderer.js +107 -0
  15. package/dist/fixture/report-plugin/state/navigationState.js +24 -0
  16. package/dist/fixture/report-plugin/ui/eyesResultsBatchLinkUI.js +51 -0
  17. package/dist/fixture/report-plugin/ui/filterManager.js +60 -0
  18. package/dist/fixture/report-plugin/ui/mockIframeRenderer.js +173 -0
  19. package/dist/fixture/report-plugin/ui/navigationUI.js +98 -0
  20. package/dist/fixture/report-plugin/ui/testDetailUI.js +269 -0
  21. package/dist/fixture/report-plugin/ui/testListUI.js +43 -0
  22. package/dist/fixture/report-plugin/utils/scrollPreserver.js +33 -0
  23. package/dist/fixture/reportRenderer.js +1 -834
  24. package/dist/fixture/reportRenderer.js.map +1 -0
  25. package/package.json +9 -8
  26. package/dist/fixture/build/rollup.config.js +0 -16
package/CHANGELOG.md CHANGED
@@ -1,5 +1,123 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.41.1](https://github.com/Applitools-Dev/sdk/compare/js/eyes-playwright@1.41.0...js/eyes-playwright@1.41.1) (2025-11-09)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * resolve html-report iframe flickering in dark mode ([#3324](https://github.com/Applitools-Dev/sdk/issues/3324)) ([3d31a90](https://github.com/Applitools-Dev/sdk/commit/3d31a90838148e93f84e7cf24ec1da25bb247140))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * @applitools/utils bumped to 1.13.0
14
+ #### Features
15
+
16
+ * restart cache and keepalive | FLD-3773 ([#3326](https://github.com/Applitools-Dev/sdk/issues/3326)) ([0fd12ca](https://github.com/Applitools-Dev/sdk/commit/0fd12ca703b4546560b563076a38f9ada24acc75))
17
+ * @applitools/dom-snapshot bumped to 4.15.0
18
+ #### Features
19
+
20
+ * add support for adopted stylesheets with nesting | FLD-3212 ([#3325](https://github.com/Applitools-Dev/sdk/issues/3325)) ([8587926](https://github.com/Applitools-Dev/sdk/commit/8587926b0d6ef820cfbd8f89ddb062a3d77f65ab))
21
+
22
+
23
+
24
+ * @applitools/dom-capture bumped to 11.6.6
25
+ #### Performance Improvements
26
+
27
+ * remove dynamic loading of Dom capture and Dom snapshot ([#3322](https://github.com/Applitools-Dev/sdk/issues/3322)) ([7d15ee9](https://github.com/Applitools-Dev/sdk/commit/7d15ee98d5d39c7e478b6bfe3e14b8eea93937e5))
28
+
29
+
30
+
31
+ * @applitools/socket bumped to 1.3.6
32
+
33
+ * @applitools/req bumped to 1.8.5
34
+
35
+ * @applitools/spec-driver-webdriver bumped to 1.5.1
36
+
37
+ * @applitools/spec-driver-playwright bumped to 1.7.7
38
+
39
+ * @applitools/spec-driver-puppeteer bumped to 1.6.7
40
+
41
+ * @applitools/nml-client bumped to 1.11.10
42
+
43
+ * @applitools/ufg-client bumped to 1.18.1
44
+
45
+ * @applitools/core-base bumped to 1.29.0
46
+ #### Features
47
+
48
+ * restart cache and keepalive | FLD-3773 ([#3326](https://github.com/Applitools-Dev/sdk/issues/3326)) ([0fd12ca](https://github.com/Applitools-Dev/sdk/commit/0fd12ca703b4546560b563076a38f9ada24acc75))
49
+
50
+
51
+
52
+ * @applitools/core bumped to 4.52.0
53
+ #### Features
54
+
55
+ * restart cache and keepalive | FLD-3773 ([#3326](https://github.com/Applitools-Dev/sdk/issues/3326)) ([0fd12ca](https://github.com/Applitools-Dev/sdk/commit/0fd12ca703b4546560b563076a38f9ada24acc75))
56
+
57
+
58
+ #### Performance Improvements
59
+
60
+ * remove dynamic loading of Dom capture and Dom snapshot ([#3322](https://github.com/Applitools-Dev/sdk/issues/3322)) ([7d15ee9](https://github.com/Applitools-Dev/sdk/commit/7d15ee98d5d39c7e478b6bfe3e14b8eea93937e5))
61
+
62
+
63
+
64
+ * @applitools/logger bumped to 2.2.5
65
+
66
+ * @applitools/image bumped to 1.2.4
67
+
68
+ * @applitools/driver bumped to 1.24.1
69
+
70
+ * @applitools/spec-driver-selenium bumped to 1.7.7
71
+
72
+ * @applitools/screenshoter bumped to 3.12.8
73
+
74
+ * @applitools/tunnel-client bumped to 1.11.3
75
+
76
+ * @applitools/ec-client bumped to 1.12.12
77
+
78
+ * @applitools/eyes bumped to 1.36.15
79
+
80
+ * @applitools/test-server bumped to 1.3.4
81
+
82
+
83
+ ## [1.41.0](https://github.com/Applitools-Dev/sdk/compare/js/eyes-playwright@1.40.7...js/eyes-playwright@1.41.0) (2025-11-03)
84
+
85
+
86
+ ### Features
87
+
88
+ * new plugin to support legacy playwright and pw 1-55 and above html reporter ([#3302](https://github.com/Applitools-Dev/sdk/issues/3302)) ([946d971](https://github.com/Applitools-Dev/sdk/commit/946d971abd0c7b86c1254bd5a7759eb1729fc7cb))
89
+
90
+
91
+ ### Dependencies
92
+
93
+ * @applitools/dom-snapshot bumped to 4.14.0
94
+ #### Features
95
+
96
+ * logging errors from dom snapshot to the backend | AD-11641 ([#3291](https://github.com/Applitools-Dev/sdk/issues/3291)) ([7f5b487](https://github.com/Applitools-Dev/sdk/commit/7f5b48701ff93bf980924c9346a8241ed87f5a56))
97
+
98
+
99
+ #### Bug Fixes
100
+
101
+ * sandbox prototype pollution | FLD-3738 ([#3310](https://github.com/Applitools-Dev/sdk/issues/3310)) ([3185558](https://github.com/Applitools-Dev/sdk/commit/31855586851d5372169aae7bf0268cec139abc59))
102
+
103
+
104
+ #### Code Refactoring
105
+
106
+ * blob generation error handling ([#2501](https://github.com/Applitools-Dev/sdk/issues/2501)) ([94bc14f](https://github.com/Applitools-Dev/sdk/commit/94bc14faf3de0fd9a8ca24af4870f839756a8aad))
107
+ * @applitools/ufg-client bumped to 1.18.0
108
+ #### Features
109
+
110
+ * logging errors from dom snapshot to the backend | AD-11641 ([#3291](https://github.com/Applitools-Dev/sdk/issues/3291)) ([7f5b487](https://github.com/Applitools-Dev/sdk/commit/7f5b48701ff93bf980924c9346a8241ed87f5a56))
111
+ * @applitools/core bumped to 4.51.0
112
+ #### Features
113
+
114
+ * logging errors from dom snapshot to the backend | AD-11641 ([#3291](https://github.com/Applitools-Dev/sdk/issues/3291)) ([7f5b487](https://github.com/Applitools-Dev/sdk/commit/7f5b48701ff93bf980924c9346a8241ed87f5a56))
115
+
116
+
117
+
118
+ * @applitools/eyes bumped to 1.36.14
119
+
120
+
3
121
  ## [1.40.7](https://github.com/Applitools-Dev/sdk/compare/js/eyes-playwright@1.40.6...js/eyes-playwright@1.40.7) (2025-10-22)
4
122
 
5
123
 
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /* eslint no-console: off */
4
+ function isDebugEnabled() {
5
+ try {
6
+ // Get query string from the topmost window (parent frame)
7
+ const topWindow = window.top || window;
8
+ const urlParams = new URLSearchParams(topWindow.location.search);
9
+ return urlParams.get('debug') === 'true';
10
+ }
11
+ catch (e) {
12
+ // Cross-origin access may throw an error, fallback to current window
13
+ try {
14
+ const urlParams = new URLSearchParams(window.location.search);
15
+ return urlParams.get('debug') === 'true';
16
+ }
17
+ catch (e2) {
18
+ return false;
19
+ }
20
+ }
21
+ }
22
+ function makeLog(prefix = '') {
23
+ const debugEnabled = isDebugEnabled();
24
+ return {
25
+ log: (...args) => {
26
+ if (!debugEnabled)
27
+ return;
28
+ const logArgs = prefix ? [prefix, ...args] : args;
29
+ console.log(...logArgs);
30
+ },
31
+ warn: (...args) => {
32
+ if (!debugEnabled)
33
+ return;
34
+ const logArgs = prefix ? [prefix, ...args] : args;
35
+ console.warn(...logArgs);
36
+ },
37
+ error: (...args) => {
38
+ // Always show errors regardless of debug mode
39
+ const logArgs = prefix ? [prefix, ...args] : args;
40
+ console.error(...logArgs);
41
+ },
42
+ };
43
+ }
44
+ exports.default = makeLog;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Shared TypeScript type definitions for Eyes Playwright Reporter Plugin
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getTestResults = void 0;
7
+ const jszip_1 = __importDefault(require("jszip"));
8
+ const statusUtils_js_1 = require("../management/statusUtils.js");
9
+ const playwrightStatusUpdater_js_1 = require("../management/playwrightStatusUpdater.js");
10
+ const reportDataManager_js_1 = require("../management/reportDataManager.js");
11
+ const log_js_1 = __importDefault(require("../core/log.js"));
12
+ const logger = (0, log_js_1.default)('[Eyes Data Parser]');
13
+ async function getTestResults() {
14
+ var _a;
15
+ // Step 1: Get Playwright report data from window object
16
+ const reportData = window.playwrightReportBase64;
17
+ const playwrightReportBase64 = typeof reportData === 'string' ? reportData : (_a = reportData === null || reportData === void 0 ? void 0 : reportData.textContent) !== null && _a !== void 0 ? _a : '';
18
+ if (!playwrightReportBase64) {
19
+ logger.error('[Eyes Data Parser] No playwrightReportBase64 found in window');
20
+ return {
21
+ testsFiles: {},
22
+ report: { files: [], stats: { expected: 0, unexpected: 0, flaky: 0, skipped: 0 } },
23
+ eyesTestResult: {},
24
+ sessionsByBatchId: {},
25
+ };
26
+ }
27
+ const base64Data = playwrightReportBase64.replace('data:application/zip;base64,', '');
28
+ // Step 2: Unzip the report data
29
+ const zip = new jszip_1.default();
30
+ await zip.loadAsync(base64Data, { base64: true });
31
+ // Step 3: Extract all test files (except report.json)
32
+ const resultsByTestFile = await Promise.all(Object.keys(zip.files)
33
+ .filter(fileName => fileName !== 'report.json')
34
+ .map(async (fileName) => {
35
+ const file = zip.files[fileName];
36
+ const content = await file.async('text');
37
+ return { [fileName]: JSON.parse(content) };
38
+ }));
39
+ const testsFiles = Object.assign({}, ...resultsByTestFile);
40
+ // Step 4: Extract report.json
41
+ const reportFileObj = zip.file('report.json');
42
+ if (!reportFileObj) {
43
+ throw new Error('[Eyes Data Parser] report.json not found in ZIP');
44
+ }
45
+ const reportFile = await reportFileObj.async('text');
46
+ const report = JSON.parse(reportFile);
47
+ // Step 5: Merge Eyes data from window.__testResultsMap
48
+ const { eyesTestResult, sessionsByBatchId } = mergeEyesData(testsFiles, report);
49
+ // Step 6: Re-zip and update window.playwrightReportBase64 with modified test statuses
50
+ // This is critical! The test statuses have been modified in memory by updatePlaywrightTestStatus
51
+ // We need to re-zip the data so Playwright's UI sees the updated statuses
52
+ await (0, reportDataManager_js_1.refreshReportData)(testsFiles, report);
53
+ return {
54
+ testsFiles,
55
+ report,
56
+ eyesTestResult,
57
+ sessionsByBatchId,
58
+ };
59
+ }
60
+ exports.getTestResults = getTestResults;
61
+ /**
62
+ * Merge Eyes data from window.__testResultsMap with Playwright tests
63
+ */
64
+ function mergeEyesData(testsFiles, report) {
65
+ const sessionsByBatchId = {};
66
+ const eyesTestResult = {};
67
+ // Check if Eyes data exists
68
+ if (!window.__testResultsMap) {
69
+ logger.warn('[Eyes Data Parser] No window.__testResultsMap found - no Eyes data to merge');
70
+ return { eyesTestResult, sessionsByBatchId };
71
+ }
72
+ // Iterate through all test files
73
+ Object.values(testsFiles).forEach(testFile => {
74
+ // Find file in report for status updates
75
+ const fileInReport = report.files.find(file => file.fileId === testFile.fileId);
76
+ if (!fileInReport)
77
+ return;
78
+ testFile.tests.forEach(test => {
79
+ const eyesResults = [];
80
+ const eyesStatuses = [];
81
+ // Look up Eyes results for each test retry
82
+ test.results.forEach((_result, index) => {
83
+ var _a;
84
+ const lookupKey = `${test.testId}--${index}`;
85
+ const eyesResultsForRetry = (_a = window.__testResultsMap) === null || _a === void 0 ? void 0 : _a[lookupKey];
86
+ if (eyesResultsForRetry) {
87
+ // Get status for this retry
88
+ const status = (0, statusUtils_js_1.getStatus)(eyesResultsForRetry);
89
+ eyesStatuses.push(status.status);
90
+ eyesResultsForRetry.forEach(eyesResult => {
91
+ // Attach retry number to Eyes result
92
+ eyesResult.playwrightRetry = _result.retry || 0;
93
+ // Add to results array
94
+ eyesResults.push(eyesResult);
95
+ // Build sessionsByBatchId for polling
96
+ if (!sessionsByBatchId[eyesResult.batchId]) {
97
+ sessionsByBatchId[eyesResult.batchId] = {
98
+ sessions: [],
99
+ apiKey: eyesResult.eyesServer.apiKey,
100
+ eyesServerUrl: eyesResult.eyesServer.eyesServerUrl,
101
+ };
102
+ }
103
+ sessionsByBatchId[eyesResult.batchId].sessions.push({
104
+ id: eyesResult.id,
105
+ accessToken: eyesResult.secretToken,
106
+ });
107
+ });
108
+ }
109
+ else {
110
+ // No Eyes results for this retry
111
+ eyesStatuses.push(null);
112
+ }
113
+ });
114
+ // If test has Eyes results, add to eyesTestResult
115
+ if (eyesResults.length > 0) {
116
+ // Update Playwright test status based on Eyes results
117
+ (0, playwrightStatusUpdater_js_1.updatePlaywrightTestStatus)(test, fileInReport, report.stats, eyesStatuses, true);
118
+ eyesTestResult[test.testId] = {
119
+ ...test,
120
+ eyesResults,
121
+ };
122
+ }
123
+ });
124
+ });
125
+ return { eyesTestResult, sessionsByBatchId };
126
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEyesInfo = void 0;
4
+ function createEyesInfo(statusInfo, addVisualIcon = true) {
5
+ const visualIcon = addVisualIcon ? `<span class="visual-test-icon">${window.__icons.visualTest}</span>` : '';
6
+ const statusBar = `<div class="status-bar ${statusInfo.status.toLowerCase()}"></div>`;
7
+ const statusText = `<span class="status-text">${statusInfo.statusText}</span>`;
8
+ return `${visualIcon}${statusBar}${statusText}`;
9
+ }
10
+ exports.createEyesInfo = createEyesInfo;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.fixEyesUrl = exports.isTestDetailMode = exports.isListMode = exports.getTestIdFromUrl = exports.getHashValueFromUrl = exports.getHashFromUrl = exports.getHashFromUrlString = exports.getHashFromCurrentUrl = void 0;
7
+ const log_1 = __importDefault(require("../core/log"));
8
+ const logger = (0, log_1.default)();
9
+ function getHashFromCurrentUrl() {
10
+ return getHashFromUrl(window.location);
11
+ }
12
+ exports.getHashFromCurrentUrl = getHashFromCurrentUrl;
13
+ function getHashFromUrlString(urlString) {
14
+ const url = new URL(urlString);
15
+ return getHashFromUrl(url);
16
+ }
17
+ exports.getHashFromUrlString = getHashFromUrlString;
18
+ function getHashFromUrl(url) {
19
+ const hash = new URLSearchParams(url.hash.replace('#', '').replace('?', ''));
20
+ return hash;
21
+ }
22
+ exports.getHashFromUrl = getHashFromUrl;
23
+ function getHashValueFromUrl(urlString, key) {
24
+ const hash = getHashFromUrlString(urlString);
25
+ return hash.get(key);
26
+ }
27
+ exports.getHashValueFromUrl = getHashValueFromUrl;
28
+ function getTestIdFromUrl(urlString) {
29
+ return getHashValueFromUrl(urlString, 'testId');
30
+ }
31
+ exports.getTestIdFromUrl = getTestIdFromUrl;
32
+ function isListMode() {
33
+ const hash = getHashFromCurrentUrl();
34
+ return !hash.get('testId');
35
+ }
36
+ exports.isListMode = isListMode;
37
+ function isTestDetailMode() {
38
+ return !isListMode();
39
+ }
40
+ exports.isTestDetailMode = isTestDetailMode;
41
+ function fixEyesUrl(url) {
42
+ try {
43
+ const urlObj = new URL(url);
44
+ urlObj.hostname = urlObj.hostname.replace('eyesapi', 'eyes');
45
+ const fixedUrl = urlObj.href;
46
+ return fixedUrl.endsWith('/') ? fixedUrl : `${fixedUrl}/`;
47
+ }
48
+ catch (error) {
49
+ logger.warn('[URL Manager] Invalid URL:', url);
50
+ return url;
51
+ }
52
+ }
53
+ exports.fixEyesUrl = fixEyesUrl;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DomChangesHandler = void 0;
4
+ const urlManager_js_1 = require("../data/urlManager.js");
5
+ class DomChangesHandler {
6
+ constructor(waitForResults, filterManager, eyesResultsBatchLinkUI, navigationUI, testListUI) {
7
+ this._navigationReadyCalled = false;
8
+ this.handleDomChanges = (mutations) => {
9
+ mutations.forEach(mutation => {
10
+ mutation.addedNodes.forEach(node => {
11
+ if (!(node instanceof HTMLElement))
12
+ return;
13
+ if (node.classList.contains('eyes-batch-link')) {
14
+ return;
15
+ }
16
+ if (node.classList.contains('htmlreport')) {
17
+ const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
18
+ this.filterManager.applyFiltersFromUrl(hash);
19
+ }
20
+ if (node.classList.contains('chip')) {
21
+ this.waitForResults.then(testResults => {
22
+ this.eyesResultsBatchLinkUI.createLinkToBatch(testResults);
23
+ });
24
+ }
25
+ const isHeaderElement = node.classList.contains('header-view-status-container') ||
26
+ node.getElementsByClassName('header-view-status-container').length > 0;
27
+ if (isHeaderElement) {
28
+ this.onNavigationReady();
29
+ }
30
+ const hasTestRows = node.getElementsByClassName('test-file-details-row').length > 0;
31
+ if (hasTestRows) {
32
+ this.waitForResults.then(testResults => {
33
+ this.testListUI.addEyesDetailsToTests(node, testResults);
34
+ });
35
+ }
36
+ });
37
+ });
38
+ };
39
+ this.waitForResults = waitForResults;
40
+ this.filterManager = filterManager;
41
+ this.eyesResultsBatchLinkUI = eyesResultsBatchLinkUI;
42
+ this.navigationUI = navigationUI;
43
+ this.testListUI = testListUI;
44
+ }
45
+ onNavigationReady() {
46
+ if (this._navigationReadyCalled) {
47
+ return;
48
+ }
49
+ this._navigationReadyCalled = true;
50
+ this.waitForResults.then(testResults => {
51
+ setTimeout(() => {
52
+ this.navigationUI.createFilters(testResults);
53
+ this.navigationUI.updateActiveStates();
54
+ }, 200);
55
+ });
56
+ }
57
+ }
58
+ exports.DomChangesHandler = DomChangesHandler;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.StatusUpdateHandler = void 0;
7
+ const urlManager_js_1 = require("../data/urlManager.js");
8
+ const log_1 = __importDefault(require("../core/log"));
9
+ const logger = (0, log_1.default)();
10
+ class StatusUpdateHandler {
11
+ constructor(scrollPreserver, eyesResultsBatchLinkUI, navigationUI, testListUI, testDetailUI) {
12
+ this.handleStatusUpdate = async (testResults) => {
13
+ // Capture scroll position before triggering window.onload
14
+ this.scrollPreserver.captureScrollPosition();
15
+ if (typeof window.onload === 'function') {
16
+ const onloadHandler = window.onload;
17
+ onloadHandler(new Event('load'));
18
+ }
19
+ setTimeout(() => {
20
+ const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
21
+ const testId = hash.get('testId');
22
+ if (!testId) {
23
+ this.eyesResultsBatchLinkUI.createLinkToBatch(testResults);
24
+ }
25
+ this.navigationUI.resetFilters();
26
+ this.navigationUI.createFilters(testResults);
27
+ this.navigationUI.updateActiveStates();
28
+ if (!testId) {
29
+ const rootElement = document.getElementById('root');
30
+ if (rootElement) {
31
+ this.testListUI.addEyesDetailsToTests(rootElement, testResults);
32
+ }
33
+ }
34
+ if (testId) {
35
+ logger.log('[Status Update Handler] Updating Eyes test result UI for testId:', testId);
36
+ const test = testResults.eyesTestResult[testId];
37
+ if (test) {
38
+ this.testDetailUI.createEyesTestResults(test);
39
+ }
40
+ }
41
+ // Restore scroll position after React has re-rendered
42
+ this.scrollPreserver.restoreScrollPosition();
43
+ }, 200);
44
+ };
45
+ this.scrollPreserver = scrollPreserver;
46
+ this.eyesResultsBatchLinkUI = eyesResultsBatchLinkUI;
47
+ this.navigationUI = navigationUI;
48
+ this.testListUI = testListUI;
49
+ this.testDetailUI = testDetailUI;
50
+ }
51
+ }
52
+ exports.StatusUpdateHandler = StatusUpdateHandler;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UrlChangeHandler = void 0;
7
+ const urlManager_js_1 = require("../data/urlManager.js");
8
+ const log_1 = __importDefault(require("../core/log"));
9
+ const logger = (0, log_1.default)();
10
+ class UrlChangeHandler {
11
+ constructor(waitForResults, navigationState, filterManager, eyesResultsBatchLinkUI, navigationUI, testDetailUI) {
12
+ this.handleUrlChange = () => {
13
+ const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
14
+ const testId = hash.get('testId');
15
+ const previousTestId = this.navigationState.activeTestId;
16
+ const fromTestToMain = !testId && previousTestId;
17
+ if (!testId) {
18
+ this.filterManager.applyFiltersFromUrl(hash);
19
+ this.navigationUI.updateActiveStates();
20
+ }
21
+ this.waitForResults.then(testResults => {
22
+ logger.log('[URL Change Handler] Handling URL change. testId:', testId);
23
+ this.eyesResultsBatchLinkUI.createLinkToBatch(testResults);
24
+ if (testId) {
25
+ const test = testResults.eyesTestResult[testId];
26
+ if (test) {
27
+ // Don't set activeTestId here - let TestDetailUI own the state setting
28
+ this.testDetailUI.createEyesTestResults(test);
29
+ }
30
+ else {
31
+ this.testDetailUI.cleanupExistingChips();
32
+ this.navigationState.clearActiveTestId();
33
+ }
34
+ }
35
+ else if (fromTestToMain) {
36
+ this.testDetailUI.cleanupExistingChips();
37
+ this.testDetailUI.cancelPendingOperations();
38
+ }
39
+ });
40
+ };
41
+ this.waitForResults = waitForResults;
42
+ this.navigationState = navigationState;
43
+ this.filterManager = filterManager;
44
+ this.eyesResultsBatchLinkUI = eyesResultsBatchLinkUI;
45
+ this.navigationUI = navigationUI;
46
+ this.testDetailUI = testDetailUI;
47
+ }
48
+ get activeTestId() {
49
+ return this.navigationState.activeTestId;
50
+ }
51
+ }
52
+ exports.UrlChangeHandler = UrlChangeHandler;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateAllPlaywrightTestStatuses = exports.updatePlaywrightTestStatus = exports.getRunStatusFromEyesStatuses = exports.getOutcomeFromEyesStatuses = void 0;
4
+ const statusUtils_js_1 = require("./statusUtils.js");
5
+ function getOutcomeFromEyesStatuses(eyesStatuses, testOutcome) {
6
+ return eyesStatuses.every(status => status === 'unresolved' || status === 'failed')
7
+ ? 'unexpected'
8
+ : eyesStatuses.some(status => status === 'unresolved' || status === 'failed')
9
+ ? 'flaky'
10
+ : testOutcome;
11
+ }
12
+ exports.getOutcomeFromEyesStatuses = getOutcomeFromEyesStatuses;
13
+ function getRunStatusFromEyesStatuses(eyesStatus, runStatus) {
14
+ return eyesStatus === 'unresolved' || eyesStatus === 'failed' ? 'failed' : runStatus;
15
+ }
16
+ exports.getRunStatusFromEyesStatuses = getRunStatusFromEyesStatuses;
17
+ function updatePlaywrightTestStatus(test, fileInReport, reportStats, eyesStatuses, updateOriginalOutcome = false) {
18
+ var _a, _b;
19
+ if (eyesStatuses.filter(status => status !== null).length > 0 &&
20
+ // test outcome: 'skipped' | 'expected' | 'unexpected' | 'flaky'
21
+ ['expected', 'flaky'].includes((_a = test.originalOutcome) !== null && _a !== void 0 ? _a : test.outcome)) {
22
+ test.results.forEach((_result, index) => {
23
+ var _a, _b;
24
+ const eyesStatus = eyesStatuses[index];
25
+ // run status: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted'
26
+ if (((_a = _result.originalStatus) !== null && _a !== void 0 ? _a : _result.status) === 'passed' && eyesStatus !== null) {
27
+ if (updateOriginalOutcome)
28
+ _result.originalStatus = _result.status;
29
+ _result.status = getRunStatusFromEyesStatuses(eyesStatus, (_b = _result.originalStatus) !== null && _b !== void 0 ? _b : _result.status);
30
+ }
31
+ });
32
+ const testInReport = fileInReport.tests.find(t => t.testId === test.testId);
33
+ if (!testInReport)
34
+ return;
35
+ if (updateOriginalOutcome) {
36
+ testInReport.originalOutcome = test.originalOutcome = test.outcome;
37
+ }
38
+ const newOutcome = getOutcomeFromEyesStatuses(eyesStatuses.filter((status) => status !== null), (_b = test.originalOutcome) !== null && _b !== void 0 ? _b : test.outcome);
39
+ if (testInReport.outcome !== newOutcome) {
40
+ reportStats[testInReport.outcome]--;
41
+ reportStats[newOutcome]++;
42
+ }
43
+ testInReport.outcome = test.outcome = newOutcome;
44
+ }
45
+ }
46
+ exports.updatePlaywrightTestStatus = updatePlaywrightTestStatus;
47
+ function updateAllPlaywrightTestStatuses(testResults) {
48
+ Object.values(testResults.testsFiles).forEach(testFile => {
49
+ const fileInReport = testResults.report.files.find(file => file.fileId === testFile.fileId);
50
+ const reportStats = testResults.report.stats;
51
+ testFile.tests.forEach(test => {
52
+ var _a;
53
+ const eyesResultsForTest = (_a = testResults.eyesTestResult[test.testId]) === null || _a === void 0 ? void 0 : _a.eyesResults;
54
+ if (!eyesResultsForTest)
55
+ return;
56
+ if (!fileInReport)
57
+ return;
58
+ // Get Eyes status for each retry
59
+ const eyesStatuses = test.results.reduce((results, _result) => {
60
+ const eyesResultsForRun = eyesResultsForTest.filter(result => result.playwrightRetry === _result.retry);
61
+ if (eyesResultsForRun.length > 0) {
62
+ results.push((0, statusUtils_js_1.getStatus)(eyesResultsForRun).status);
63
+ }
64
+ else {
65
+ results.push(null);
66
+ }
67
+ return results;
68
+ }, []);
69
+ updatePlaywrightTestStatus(test, fileInReport, reportStats, eyesStatuses);
70
+ });
71
+ });
72
+ }
73
+ exports.updateAllPlaywrightTestStatuses = updateAllPlaywrightTestStatuses;