@applitools/eyes-playwright 1.40.7 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/fixture/report-plugin/core/log.js +44 -0
- package/dist/fixture/report-plugin/core/types.js +5 -0
- package/dist/fixture/report-plugin/data/dataParser.js +126 -0
- package/dist/fixture/report-plugin/data/uiUtils.js +10 -0
- package/dist/fixture/report-plugin/data/urlManager.js +53 -0
- package/dist/fixture/report-plugin/handlers/domChangesHandler.js +58 -0
- package/dist/fixture/report-plugin/handlers/statusUpdateHandler.js +52 -0
- package/dist/fixture/report-plugin/handlers/urlChangeHandler.js +52 -0
- package/dist/fixture/report-plugin/management/playwrightStatusUpdater.js +73 -0
- package/dist/fixture/report-plugin/management/pollingManager.js +235 -0
- package/dist/fixture/report-plugin/management/reportDataManager.js +32 -0
- package/dist/fixture/report-plugin/management/statusUtils.js +102 -0
- package/dist/fixture/report-plugin/reportRenderer.js +107 -0
- package/dist/fixture/report-plugin/state/navigationState.js +24 -0
- package/dist/fixture/report-plugin/ui/eyesResultsBatchLinkUI.js +51 -0
- package/dist/fixture/report-plugin/ui/filterManager.js +60 -0
- package/dist/fixture/report-plugin/ui/mockIframeRenderer.js +39 -0
- package/dist/fixture/report-plugin/ui/navigationUI.js +98 -0
- package/dist/fixture/report-plugin/ui/testDetailUI.js +265 -0
- package/dist/fixture/report-plugin/ui/testListUI.js +43 -0
- package/dist/fixture/report-plugin/utils/scrollPreserver.js +33 -0
- package/dist/fixture/reportRenderer.js +1 -834
- package/dist/fixture/reportRenderer.js.map +1 -0
- package/package.json +6 -5
- package/dist/fixture/build/rollup.config.js +0 -16
|
@@ -0,0 +1,98 @@
|
|
|
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.NavigationUI = void 0;
|
|
7
|
+
const statusUtils_js_1 = require("../management/statusUtils.js");
|
|
8
|
+
const log_1 = __importDefault(require("../core/log"));
|
|
9
|
+
const logger = (0, log_1.default)();
|
|
10
|
+
class NavigationUI {
|
|
11
|
+
constructor(filterManager) {
|
|
12
|
+
this._filtersCreated = false;
|
|
13
|
+
this.filterManager = filterManager;
|
|
14
|
+
}
|
|
15
|
+
createFilters(testResults) {
|
|
16
|
+
if (this._filtersCreated) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (document.getElementById('eyes-filter') && document.getElementById('unresolved-filter')) {
|
|
20
|
+
this._filtersCreated = true;
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const allFilters = document.getElementsByClassName('subnav-item');
|
|
24
|
+
const navTextButtons = Array.from(allFilters).filter(filter => filter.tagName === 'A');
|
|
25
|
+
const lastFilter = navTextButtons[navTextButtons.length - 1];
|
|
26
|
+
if (!lastFilter) {
|
|
27
|
+
logger.warn('[Eyes Navigation] No subnav items found, cannot create filters');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const counts = (0, statusUtils_js_1.countTestsByStatus)(testResults.eyesTestResult);
|
|
31
|
+
const eyesFilter = lastFilter.cloneNode(true);
|
|
32
|
+
eyesFilter.id = 'eyes-filter';
|
|
33
|
+
eyesFilter.firstChild.textContent = 'Eyes ';
|
|
34
|
+
eyesFilter.lastChild.textContent = String(counts.total);
|
|
35
|
+
eyesFilter.href = '#eyes=true';
|
|
36
|
+
this._placeElementAfter(eyesFilter, lastFilter);
|
|
37
|
+
const unresolvedFilter = lastFilter.cloneNode(true);
|
|
38
|
+
unresolvedFilter.id = 'unresolved-filter';
|
|
39
|
+
unresolvedFilter.firstChild.textContent = 'Unresolved ';
|
|
40
|
+
unresolvedFilter.lastChild.textContent = String(counts.unresolved);
|
|
41
|
+
unresolvedFilter.href = '#unresolved=true';
|
|
42
|
+
this._placeElementAfter(unresolvedFilter, eyesFilter);
|
|
43
|
+
this._filtersCreated = true;
|
|
44
|
+
}
|
|
45
|
+
updateFilterCounts(testResults) {
|
|
46
|
+
if (!this._filtersCreated) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const counts = (0, statusUtils_js_1.countTestsByStatus)(testResults.eyesTestResult);
|
|
50
|
+
const eyesFilter = document.getElementById('eyes-filter');
|
|
51
|
+
if (eyesFilter && eyesFilter.lastChild) {
|
|
52
|
+
eyesFilter.lastChild.textContent = String(counts.total);
|
|
53
|
+
}
|
|
54
|
+
const unresolvedFilter = document.getElementById('unresolved-filter');
|
|
55
|
+
if (unresolvedFilter && unresolvedFilter.lastChild) {
|
|
56
|
+
unresolvedFilter.lastChild.textContent = String(counts.unresolved);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
areFiltersCreated() {
|
|
60
|
+
return this._filtersCreated;
|
|
61
|
+
}
|
|
62
|
+
resetFilters() {
|
|
63
|
+
this._filtersCreated = false;
|
|
64
|
+
}
|
|
65
|
+
updateActiveStates() {
|
|
66
|
+
if (!this._filtersCreated) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const eyesFilter = document.getElementById('eyes-filter');
|
|
70
|
+
const unresolvedFilter = document.getElementById('unresolved-filter');
|
|
71
|
+
if (eyesFilter) {
|
|
72
|
+
if (this.filterManager.isEyesFilterActive()) {
|
|
73
|
+
eyesFilter.classList.add('selected');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
eyesFilter.classList.remove('selected');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (unresolvedFilter) {
|
|
80
|
+
if (this.filterManager.isUnresolvedFilterActive()) {
|
|
81
|
+
unresolvedFilter.classList.add('selected');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
unresolvedFilter.classList.remove('selected');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
_placeElementAfter(newElement, referenceElement) {
|
|
89
|
+
const nextSibling = referenceElement.nextSibling;
|
|
90
|
+
if (nextSibling) {
|
|
91
|
+
referenceElement.parentNode.insertBefore(newElement, nextSibling);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
referenceElement.parentNode.appendChild(newElement);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.NavigationUI = NavigationUI;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test Detail UI - Manages Eyes test result display in Playwright test detail view
|
|
4
|
+
*
|
|
5
|
+
* This module handles creating and managing Eyes test result iframes in the test detail page.
|
|
6
|
+
* It observes DOM changes to detect when test detail pages are ready, manages retry tab
|
|
7
|
+
* selection changes, and creates the appropriate iframe or mock iframe for each Eyes session.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.TestDetailUI = void 0;
|
|
14
|
+
const urlManager_js_1 = require("../data/urlManager.js");
|
|
15
|
+
const statusUtils_js_1 = require("../management/statusUtils.js");
|
|
16
|
+
const uiUtils_js_1 = require("../data/uiUtils.js");
|
|
17
|
+
const mockIframeRenderer_js_1 = require("./mockIframeRenderer.js");
|
|
18
|
+
const log_1 = __importDefault(require("../core/log"));
|
|
19
|
+
const logger = (0, log_1.default)();
|
|
20
|
+
class TestDetailUI {
|
|
21
|
+
constructor(navigationState, options = {}) {
|
|
22
|
+
this.navigationState = navigationState;
|
|
23
|
+
this._pendingObserver = null;
|
|
24
|
+
this._retryObserver = null;
|
|
25
|
+
this._lastCreateTimestamp = 0;
|
|
26
|
+
this.useTestMode = options.useTestMode || false;
|
|
27
|
+
}
|
|
28
|
+
cancelPendingOperations() {
|
|
29
|
+
if (this._pendingObserver) {
|
|
30
|
+
this._pendingObserver.disconnect();
|
|
31
|
+
this._pendingObserver = null;
|
|
32
|
+
}
|
|
33
|
+
if (this._retryObserver) {
|
|
34
|
+
this._retryObserver.disconnect();
|
|
35
|
+
this._retryObserver = null;
|
|
36
|
+
}
|
|
37
|
+
// Clear activeTestId when canceling operations (navigation away)
|
|
38
|
+
this.navigationState.clearActiveTestId();
|
|
39
|
+
}
|
|
40
|
+
createEyesTestResults(test) {
|
|
41
|
+
logger.log('[TestDetailUI] Creating Eyes test results for testId:', test.testId);
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
// Debounce: skip if same test was just created within 200ms
|
|
44
|
+
if (this.navigationState.activeTestId === test.testId && now - this._lastCreateTimestamp < 200) {
|
|
45
|
+
logger.log('[TestDetailUI] Debounce: skipping duplicate call within 200ms');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.cancelPendingOperations();
|
|
49
|
+
const currentTestId = test.testId;
|
|
50
|
+
// Set activeTestId before timestamp to ensure proper debounce check
|
|
51
|
+
this.navigationState.setActiveTestId(currentTestId);
|
|
52
|
+
this._lastCreateTimestamp = now;
|
|
53
|
+
let hasCreatedResults = false;
|
|
54
|
+
const isCorrectTestPage = () => {
|
|
55
|
+
const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
|
|
56
|
+
const urlTestId = hash.get('testId');
|
|
57
|
+
return urlTestId === currentTestId;
|
|
58
|
+
};
|
|
59
|
+
const attemptToCreateResults = () => {
|
|
60
|
+
if (hasCreatedResults)
|
|
61
|
+
return false;
|
|
62
|
+
if (!isCorrectTestPage()) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
let tabContent = document.querySelector('.tab-content[role="tabpanel"]');
|
|
66
|
+
if (!tabContent) {
|
|
67
|
+
tabContent = document.querySelector('.tab-content > .test-result');
|
|
68
|
+
}
|
|
69
|
+
logger.log('[TestDetailUI] Attempting to create Eyes test results. Tab content found:', !!tabContent);
|
|
70
|
+
const chipsInTab = tabContent === null || tabContent === void 0 ? void 0 : tabContent.getElementsByClassName('chip');
|
|
71
|
+
if (tabContent && chipsInTab && chipsInTab.length > 0) {
|
|
72
|
+
if (!isCorrectTestPage()) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
hasCreatedResults = true;
|
|
76
|
+
this.onTestResultPageReady(test);
|
|
77
|
+
this._pendingObserver = null;
|
|
78
|
+
// Don't clear activeTestId here - keep it set for debouncing
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
};
|
|
83
|
+
if (attemptToCreateResults())
|
|
84
|
+
return;
|
|
85
|
+
const testCaseColumn = document.querySelector('.test-case-column');
|
|
86
|
+
const htmlReportElement = document.getElementsByClassName('htmlreport')[0];
|
|
87
|
+
const observeTarget = testCaseColumn || htmlReportElement;
|
|
88
|
+
if (!observeTarget) {
|
|
89
|
+
logger.warn('No observe target found, retrying after delay');
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
if (this.navigationState.activeTestId === currentTestId && isCorrectTestPage()) {
|
|
92
|
+
this.createEyesTestResults(test);
|
|
93
|
+
}
|
|
94
|
+
}, 200);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const mutationObserver = new MutationObserver(() => {
|
|
98
|
+
if (attemptToCreateResults()) {
|
|
99
|
+
mutationObserver.disconnect();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this._pendingObserver = mutationObserver;
|
|
103
|
+
mutationObserver.observe(observeTarget, {
|
|
104
|
+
childList: true,
|
|
105
|
+
subtree: true,
|
|
106
|
+
});
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
if (attemptToCreateResults()) {
|
|
109
|
+
mutationObserver.disconnect();
|
|
110
|
+
}
|
|
111
|
+
else if (isCorrectTestPage()) {
|
|
112
|
+
logger.warn(`Timeout: Failed to create Eyes results for test ${currentTestId}`);
|
|
113
|
+
}
|
|
114
|
+
}, 300);
|
|
115
|
+
}
|
|
116
|
+
onTestResultPageReady(test) {
|
|
117
|
+
const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
|
|
118
|
+
const urlTestId = hash.get('testId');
|
|
119
|
+
if (urlTestId !== test.testId) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const shouldFilterUnresolved = new URLSearchParams(window.location.search).get('unresolved');
|
|
123
|
+
const frontendPath = new URLSearchParams(window.location.search).get('frontendPath');
|
|
124
|
+
const iframeSrc = `app/html-report/index.html?${frontendPath != null ? `frontendPath=${frontendPath}&` : ''}${shouldFilterUnresolved ? 'unresolved=true&' : ''}cache=${Date.now()}&`;
|
|
125
|
+
let currentSelectedRetryIndex = Array.from(document.getElementsByClassName('tabbed-pane-tab-element')).findIndex(tab => tab.classList.contains('selected'));
|
|
126
|
+
if (currentSelectedRetryIndex === -1) {
|
|
127
|
+
currentSelectedRetryIndex = 0;
|
|
128
|
+
}
|
|
129
|
+
this.createEyesResultIframes(test, iframeSrc, currentSelectedRetryIndex, shouldFilterUnresolved);
|
|
130
|
+
this.observeRetryTabSelectionChanged(test, iframeSrc, shouldFilterUnresolved);
|
|
131
|
+
}
|
|
132
|
+
observeRetryTabSelectionChanged(test, iframeSrc, shouldFilterUnresolved) {
|
|
133
|
+
const retryTabs = document.getElementsByClassName('tabbed-pane-tab-element');
|
|
134
|
+
if (retryTabs.length === 0)
|
|
135
|
+
return;
|
|
136
|
+
let selectedRetryIndex = Array.from(retryTabs).findIndex(tab => tab.classList.contains('selected'));
|
|
137
|
+
const mutationObserver = new MutationObserver(() => {
|
|
138
|
+
const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
|
|
139
|
+
const urlTestId = hash.get('testId');
|
|
140
|
+
if (urlTestId !== test.testId) {
|
|
141
|
+
mutationObserver.disconnect();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const newSelectedRetryIndex = Array.from(retryTabs).findIndex(tab => tab.classList.contains('selected'));
|
|
145
|
+
if (newSelectedRetryIndex === selectedRetryIndex)
|
|
146
|
+
return;
|
|
147
|
+
selectedRetryIndex = newSelectedRetryIndex;
|
|
148
|
+
this.createEyesResultIframes(test, iframeSrc, selectedRetryIndex, shouldFilterUnresolved);
|
|
149
|
+
});
|
|
150
|
+
this._retryObserver = mutationObserver;
|
|
151
|
+
if (retryTabs[selectedRetryIndex]) {
|
|
152
|
+
mutationObserver.observe(retryTabs[selectedRetryIndex], {
|
|
153
|
+
attributes: true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
createTemplateChip() {
|
|
158
|
+
const firstChip = document.getElementsByClassName('chip')[0];
|
|
159
|
+
const chipTemplate = firstChip.cloneNode(true);
|
|
160
|
+
chipTemplate.classList.add('eyes-test-results');
|
|
161
|
+
const chipTemplateHeader = chipTemplate.querySelector('.chip-header');
|
|
162
|
+
chipTemplateHeader.setAttribute('title', 'Eyes Test Results');
|
|
163
|
+
chipTemplateHeader.removeChild(chipTemplateHeader.lastChild);
|
|
164
|
+
const chipHeaderText = document.createElement('span');
|
|
165
|
+
chipHeaderText.classList.add('chip-header-text');
|
|
166
|
+
chipTemplateHeader.appendChild(chipHeaderText);
|
|
167
|
+
chipTemplateHeader.onclick = (event) => {
|
|
168
|
+
const target = event.currentTarget;
|
|
169
|
+
target.classList.toggle('expanded-false');
|
|
170
|
+
target.classList.toggle('expanded-true');
|
|
171
|
+
target.parentNode.querySelector('.chip-body').classList.toggle('hidden');
|
|
172
|
+
};
|
|
173
|
+
const chipTemplateBody = chipTemplate.querySelector('.chip-body');
|
|
174
|
+
chipTemplateBody.innerHTML = '';
|
|
175
|
+
return { chipTemplate, chipTemplateHeader, firstChip };
|
|
176
|
+
}
|
|
177
|
+
createChipForTest(result, chipTemplate, chipTemplateHeader) {
|
|
178
|
+
const chip = chipTemplate.cloneNode(true);
|
|
179
|
+
chip.setAttribute('value', result.id);
|
|
180
|
+
const chipHeader = chip.querySelector('.chip-header');
|
|
181
|
+
chipHeader.onclick = chipTemplateHeader.onclick;
|
|
182
|
+
const sessionText = `${result.hostApp} ${result.hostDisplaySize.width} x ${result.hostDisplaySize.height}`;
|
|
183
|
+
const chipHeaderText = chip.querySelector('.chip-header-text');
|
|
184
|
+
chipHeaderText.innerHTML = `Eyes Test Results <span class="test-header-info"> - ${sessionText} <div class="eyes-info">${(0, uiUtils_js_1.createEyesInfo)((0, statusUtils_js_1.getSingleSessionStatus)(result), false)}</div><a href=${result.appUrls.session} target='_blank' title='View in Eyes Dashboard'>
|
|
185
|
+
<span>${window.__icons.link}</span>
|
|
186
|
+
</a></span>`;
|
|
187
|
+
const chipLink = chip.querySelector('.chip-header-text a');
|
|
188
|
+
chipLink.onclick = event => event.stopPropagation();
|
|
189
|
+
return chip;
|
|
190
|
+
}
|
|
191
|
+
createTestIframe(iframeSrc, result) {
|
|
192
|
+
const iframe = document.createElement('iframe');
|
|
193
|
+
iframe.classList.add('eyes-session-result');
|
|
194
|
+
iframe.setAttribute('value', result.id);
|
|
195
|
+
const url = (0, urlManager_js_1.fixEyesUrl)(result.eyesServer.eyesServerUrl);
|
|
196
|
+
iframe.onload = () => {
|
|
197
|
+
const iframeUrl = `${url}app/html-report/index.html`;
|
|
198
|
+
iframe.contentWindow.postMessage({ call: 'sendValue', value: result }, iframeUrl);
|
|
199
|
+
iframe.classList.add('ready');
|
|
200
|
+
window.addEventListener('message', async (event) => {
|
|
201
|
+
if (event.origin !== url && `${event.origin}/` !== url)
|
|
202
|
+
return;
|
|
203
|
+
if (event.data === 'forceRefreshData') {
|
|
204
|
+
// Handle force refresh if needed
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
iframe.src = `${url}${iframeSrc}`;
|
|
209
|
+
return iframe;
|
|
210
|
+
}
|
|
211
|
+
createEyesResultIframes(test, iframeSrc, selectedRetryIndex, shouldFilterUnresolved) {
|
|
212
|
+
const hash = (0, urlManager_js_1.getHashFromCurrentUrl)();
|
|
213
|
+
const urlTestId = hash.get('testId');
|
|
214
|
+
if (urlTestId !== test.testId) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const existingChips = document.getElementsByClassName('eyes-test-results');
|
|
218
|
+
if (existingChips.length > 0) {
|
|
219
|
+
const existingSessionIds = Array.from(existingChips).map(chip => chip.getAttribute('value'));
|
|
220
|
+
const expectedSessionIds = test.eyesResults
|
|
221
|
+
.filter((r) => r.playwrightRetry === selectedRetryIndex)
|
|
222
|
+
.filter((r) => !shouldFilterUnresolved || (0, statusUtils_js_1.getSingleSessionStatus)(r).status === 'unresolved')
|
|
223
|
+
.map((r) => r.id);
|
|
224
|
+
const hasAllExpectedIframes = existingSessionIds.length === expectedSessionIds.length &&
|
|
225
|
+
expectedSessionIds.every((id) => existingSessionIds.includes(id));
|
|
226
|
+
if (hasAllExpectedIframes) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
Array.from(existingChips).forEach(chip => chip.remove());
|
|
230
|
+
}
|
|
231
|
+
const { chipTemplate, chipTemplateHeader, firstChip } = this.createTemplateChip();
|
|
232
|
+
let createdIframes = 0;
|
|
233
|
+
test.eyesResults.forEach((result) => {
|
|
234
|
+
if (result.playwrightRetry !== selectedRetryIndex)
|
|
235
|
+
return;
|
|
236
|
+
if (shouldFilterUnresolved && (0, statusUtils_js_1.getSingleSessionStatus)(result).status !== 'unresolved')
|
|
237
|
+
return;
|
|
238
|
+
const chip = this.createChipForTest(result, chipTemplate, chipTemplateHeader);
|
|
239
|
+
const chipBody = chip.querySelector('.chip-body');
|
|
240
|
+
let bodyElement;
|
|
241
|
+
if (result.steps === 0) {
|
|
242
|
+
bodyElement = document.createElement('div');
|
|
243
|
+
bodyElement.innerHTML = 'No steps were executed for this test';
|
|
244
|
+
chipBody === null || chipBody === void 0 ? void 0 : chipBody.classList.add('empty');
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
bodyElement = this.useTestMode ? (0, mockIframeRenderer_js_1.createMockIframe)(result) : this.createTestIframe(iframeSrc, result);
|
|
248
|
+
}
|
|
249
|
+
chipBody === null || chipBody === void 0 ? void 0 : chipBody.appendChild(bodyElement);
|
|
250
|
+
firstChip.parentNode.appendChild(chip);
|
|
251
|
+
if (createdIframes > 0) {
|
|
252
|
+
const chipHeader = chip.querySelector('.chip-header');
|
|
253
|
+
chipHeader.click();
|
|
254
|
+
}
|
|
255
|
+
++createdIframes;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
cleanupExistingChips() {
|
|
259
|
+
const eyesTestResults = document.getElementsByClassName('eyes-test-results');
|
|
260
|
+
if (eyesTestResults.length > 0) {
|
|
261
|
+
Array.from(eyesTestResults).forEach(eyesTestResult => eyesTestResult.remove());
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
exports.TestDetailUI = TestDetailUI;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestListUI = void 0;
|
|
4
|
+
const statusUtils_js_1 = require("../management/statusUtils.js");
|
|
5
|
+
const urlManager_js_1 = require("../data/urlManager.js");
|
|
6
|
+
const uiUtils_js_1 = require("../data/uiUtils.js");
|
|
7
|
+
class TestListUI {
|
|
8
|
+
constructor() { }
|
|
9
|
+
addEyesDetailsToTests(node, testResults) {
|
|
10
|
+
const testsElements = Array.from(node.getElementsByClassName('test-file-details-row'));
|
|
11
|
+
testsElements.forEach(testElement => {
|
|
12
|
+
if (!(testElement instanceof HTMLElement))
|
|
13
|
+
return;
|
|
14
|
+
const anchor = testElement.getElementsByTagName('a')[0];
|
|
15
|
+
if (!(anchor === null || anchor === void 0 ? void 0 : anchor.href))
|
|
16
|
+
return;
|
|
17
|
+
const testId = (0, urlManager_js_1.getTestIdFromUrl)(anchor.href);
|
|
18
|
+
if (!testId)
|
|
19
|
+
return;
|
|
20
|
+
const test = testResults.eyesTestResult[testId];
|
|
21
|
+
if (!test)
|
|
22
|
+
return;
|
|
23
|
+
const statusInfo = (0, statusUtils_js_1.getStatus)(test.eyesResults);
|
|
24
|
+
const testRow = testElement.parentNode;
|
|
25
|
+
if (!testRow)
|
|
26
|
+
return;
|
|
27
|
+
testRow.classList.add('eyes-test');
|
|
28
|
+
testRow.setAttribute('status', statusInfo.status.toLowerCase());
|
|
29
|
+
if (testElement.getElementsByClassName('eyes-info-link').length > 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const eyesInfoLink = document.createElement('a');
|
|
33
|
+
eyesInfoLink.classList.add('eyes-info-link');
|
|
34
|
+
eyesInfoLink.href = anchor.href;
|
|
35
|
+
const eyesInfo = document.createElement('div');
|
|
36
|
+
eyesInfo.classList.add('eyes-info');
|
|
37
|
+
eyesInfo.innerHTML = (0, uiUtils_js_1.createEyesInfo)(statusInfo);
|
|
38
|
+
eyesInfoLink.appendChild(eyesInfo);
|
|
39
|
+
testElement.appendChild(eyesInfoLink);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.TestListUI = TestListUI;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scroll Preserver - Utility to preserve and restore scroll positions
|
|
4
|
+
*
|
|
5
|
+
* When window.onload() is triggered, React re-renders and may scroll to top.
|
|
6
|
+
* This utility captures scroll positions before the re-render and restores them after.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ScrollPreserver = void 0;
|
|
10
|
+
class ScrollPreserver {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.savedScrollPosition = null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Capture the current scroll position of the window
|
|
16
|
+
*/
|
|
17
|
+
captureScrollPosition() {
|
|
18
|
+
this.savedScrollPosition = {
|
|
19
|
+
x: window.scrollX || window.pageXOffset,
|
|
20
|
+
y: window.scrollY || window.pageYOffset,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Restore the previously captured scroll position
|
|
25
|
+
*/
|
|
26
|
+
restoreScrollPosition() {
|
|
27
|
+
if (this.savedScrollPosition) {
|
|
28
|
+
window.scrollTo(this.savedScrollPosition.x, this.savedScrollPosition.y);
|
|
29
|
+
this.savedScrollPosition = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.ScrollPreserver = ScrollPreserver;
|