@brightspace-ui/testing 1.38.2 → 1.39.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/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@brightspace-ui/testing",
|
|
3
3
|
"description": "Utilities for testing front-end components and applications",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.39.0",
|
|
6
6
|
"repository": "https://github.com/BrightspaceUI/testing.git",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "npm run lint:eslint && npm run lint:style",
|
package/src/browser/vdiff.js
CHANGED
|
@@ -89,12 +89,11 @@ async function ScreenshotAndCompare(opts = {}) {
|
|
|
89
89
|
({ rect, fullPage } = findLargestRect(elemsToInclude, margin));
|
|
90
90
|
}
|
|
91
91
|
const slowDuration = this.test.slow();
|
|
92
|
+
const altTests = [];
|
|
93
|
+
if (opts.allColorModes) altTests.push('dark');
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.test.timeout(0);
|
|
96
|
-
result = await executeServerCommand('brightspace-visual-diff-compare-resize', { name });
|
|
97
|
-
}
|
|
95
|
+
this.test.timeout(0);
|
|
96
|
+
const result = await executeServerCommand('brightspace-visual-diff-compare', { altTests, name, fullPage, rect, slowDuration });
|
|
98
97
|
|
|
99
98
|
if (window.d2lTest) document.documentElement.classList.remove('screenshot');
|
|
100
99
|
|
|
@@ -8,9 +8,9 @@ export class TestInfoManager {
|
|
|
8
8
|
return testInfoMap.get(this.key);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
set(testInfo) {
|
|
11
|
+
set(testInfo, alt = 'default') {
|
|
12
12
|
if (testInfoMap.has(this.key)) {
|
|
13
|
-
const info = this.get();
|
|
13
|
+
const info = this.get()?.[alt] || {};
|
|
14
14
|
testInfo.slowDuration = info.slowDuration;
|
|
15
15
|
|
|
16
16
|
if (info.golden || testInfo.golden) {
|
|
@@ -21,7 +21,8 @@ export class TestInfoManager {
|
|
|
21
21
|
}
|
|
22
22
|
testInfo.diff = testInfo.diff || info.diff;
|
|
23
23
|
}
|
|
24
|
-
testInfoMap.
|
|
24
|
+
const tests = testInfoMap.get(this.key) || {};
|
|
25
|
+
testInfoMap.set(this.key, { ...tests, [alt]: testInfo });
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -9,6 +9,17 @@ import { TestInfoManager } from './visual-diff-info.js';
|
|
|
9
9
|
const isCI = !!env['CI'];
|
|
10
10
|
const DEFAULT_TOLERANCE = 0; // TODO: Support tolerance override?
|
|
11
11
|
|
|
12
|
+
const ALT_TESTS = {
|
|
13
|
+
dark: {
|
|
14
|
+
set() {
|
|
15
|
+
document.documentElement.setAttribute('data-color-mode', 'dark');
|
|
16
|
+
},
|
|
17
|
+
reset() {
|
|
18
|
+
document.documentElement.removeAttribute('data-color-mode');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
12
23
|
async function checkFileExists(fileName) {
|
|
13
24
|
try {
|
|
14
25
|
await access(fileName, constants.F_OK);
|
|
@@ -67,6 +78,45 @@ function getResizedPng(xName, yName, original, newSize) {
|
|
|
67
78
|
return resized;
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
let bestPosition = null;
|
|
82
|
+
async function getBestResizePng(screenshotImage, goldenImage) {
|
|
83
|
+
const newWidth = Math.max(screenshotImage.width, goldenImage.width);
|
|
84
|
+
const newHeight = Math.max(screenshotImage.height, goldenImage.height);
|
|
85
|
+
const newSize = { width: newWidth, height: newHeight };
|
|
86
|
+
|
|
87
|
+
if (bestPosition !== null) {
|
|
88
|
+
const [y, x] = bestPosition.split('-');
|
|
89
|
+
const screenshot = getResizedPng(x, y, screenshotImage, newSize);
|
|
90
|
+
const golden = getResizedPng(x, y, goldenImage, newSize);
|
|
91
|
+
const { diff, pixelsDiff } = getPixelsDiff(screenshot, golden);
|
|
92
|
+
return { screenshot, golden, diff, pixelsDiff };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const newScreenshots = await createComparisonPNGs(screenshotImage, newSize);
|
|
96
|
+
const newGoldens = await createComparisonPNGs(goldenImage, newSize);
|
|
97
|
+
|
|
98
|
+
let bestIndex = -1;
|
|
99
|
+
let bestDiffImage = null;
|
|
100
|
+
let bestPixelsDiff = Number.MAX_SAFE_INTEGER;
|
|
101
|
+
for (let i = 0; i < newScreenshots.length; i++) {
|
|
102
|
+
const { diff, pixelsDiff } = getPixelsDiff(newScreenshots[i].png, newGoldens[i].png);
|
|
103
|
+
|
|
104
|
+
if (pixelsDiff < bestPixelsDiff) {
|
|
105
|
+
bestIndex = i;
|
|
106
|
+
bestDiffImage = diff;
|
|
107
|
+
bestPixelsDiff = pixelsDiff;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
bestPosition = newScreenshots[bestIndex].position;
|
|
112
|
+
return {
|
|
113
|
+
screenshot: newScreenshots[bestIndex].png,
|
|
114
|
+
golden: newGoldens[bestIndex].png,
|
|
115
|
+
diff: bestDiffImage,
|
|
116
|
+
pixelsDiff: bestPixelsDiff
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
70
120
|
async function createComparisonPNGs(original, newSize) {
|
|
71
121
|
const resizedPNGs = [];
|
|
72
122
|
[ 'top', 'center', 'bottom' ].forEach(y => {
|
|
@@ -145,156 +195,156 @@ export function visualDiff({ updateGoldens = false, runSubset = false } = {}) {
|
|
|
145
195
|
const { dir, newName } = extractTestPartsFromName(payload.name);
|
|
146
196
|
const testPath = dirname(session.testFile).replace(rootDir, '');
|
|
147
197
|
const newPath = join(rootDir, PATHS.VDIFF_ROOT, testPath, dir);
|
|
148
|
-
const
|
|
149
|
-
const
|
|
198
|
+
const goldenFile = join(newPath, PATHS.GOLDEN, browser, newName);
|
|
199
|
+
const passFile = join(newPath, PATHS.PASS, browser, newName);
|
|
150
200
|
const screenshotFile = join(newPath, PATHS.FAIL, browser, newName);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
201
|
+
function getFileName(base, alt = 'default') {
|
|
202
|
+
return `${base}${alt === 'default' ? '' : `.${alt}`}.png`;
|
|
203
|
+
}
|
|
204
|
+
const tests = ['default', ...(payload.altTests || [])];
|
|
205
|
+
const rootLength = join(rootDir, PATHS.VDIFF_ROOT).length + 1;
|
|
206
|
+
|
|
207
|
+
if (command !== 'brightspace-visual-diff-compare') return;
|
|
208
|
+
if (!isCI) { // CI will be a fresh .vdiff folder each time and only one run
|
|
209
|
+
if (session.testRun !== currentRun) {
|
|
210
|
+
currentRun = session.testRun;
|
|
211
|
+
clearedDirs.clear();
|
|
212
|
+
}
|
|
159
213
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
await clearedDirs.get(newPath);
|
|
214
|
+
if (runSubset || currentRun > 0) {
|
|
215
|
+
if (!clearedDirs.has(newPath)) {
|
|
216
|
+
clearedDirs.set(newPath, clearDir(updateGoldens, newPath));
|
|
165
217
|
}
|
|
218
|
+
await clearedDirs.get(newPath);
|
|
166
219
|
}
|
|
220
|
+
}
|
|
167
221
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
222
|
+
const screenshotOpts = {
|
|
223
|
+
animations: 'disabled'
|
|
224
|
+
};
|
|
171
225
|
|
|
172
|
-
|
|
173
|
-
|
|
226
|
+
if (payload.fullPage) screenshotOpts.fullPage = true;
|
|
227
|
+
if (payload.rect) screenshotOpts.clip = payload.rect;
|
|
174
228
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
229
|
+
const page = session.browser.getPage(session.id);
|
|
230
|
+
async function takeScreenshot(alt = 'default') {
|
|
231
|
+
const path = getFileName(updateGoldens ? goldenFile : screenshotFile, alt);
|
|
232
|
+
if (ALT_TESTS[alt]?.set) page.evaluate(ALT_TESTS[alt].set);
|
|
233
|
+
const screenshot = await page.screenshot({ path, ...screenshotOpts });
|
|
234
|
+
if (ALT_TESTS[alt]?.reset) page.evaluate(ALT_TESTS[alt].reset);
|
|
235
|
+
return screenshot;
|
|
236
|
+
}
|
|
180
237
|
|
|
181
|
-
|
|
238
|
+
const screenshotFileBuffers = {};
|
|
239
|
+
for (const test of tests) screenshotFileBuffers[test] = await takeScreenshot(test);
|
|
182
240
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
241
|
+
if (updateGoldens) {
|
|
242
|
+
return { pass: true };
|
|
243
|
+
}
|
|
186
244
|
|
|
187
|
-
|
|
245
|
+
async function runCompareTest(alt = 'default') {
|
|
246
|
+
const screenshotFileName = getFileName(screenshotFile, alt);
|
|
247
|
+
const goldenFileName = getFileName(goldenFile, alt);
|
|
248
|
+
const passFileName = getFileName(passFile, alt);
|
|
188
249
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
width: screenshotImage.width
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
const goldenExists = await checkFileExists(goldenFileName);
|
|
201
|
-
if (!goldenExists) {
|
|
202
|
-
return { pass: false, message: 'No golden exists. Use the "--golden" CLI flag to re-run and re-generate goldens.' };
|
|
250
|
+
const screenshotImage = PNG.sync.read(screenshotFileBuffers[alt]);
|
|
251
|
+
infoManager.set({
|
|
252
|
+
slowDuration: payload.slowDuration,
|
|
253
|
+
new: {
|
|
254
|
+
height: screenshotImage.height,
|
|
255
|
+
path: screenshotFileName.substring(rootLength),
|
|
256
|
+
width: screenshotImage.width
|
|
203
257
|
}
|
|
258
|
+
}, alt);
|
|
204
259
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
golden: {
|
|
209
|
-
height: goldenImage.height,
|
|
210
|
-
path: goldenFileName.substring(rootLength),
|
|
211
|
-
width: goldenImage.width
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
if (screenshotImage.width === goldenImage.width && screenshotImage.height === goldenImage.height) {
|
|
216
|
-
const goldenSize = (await stat(goldenFileName)).size;
|
|
217
|
-
const screenshotSize = (await stat(screenshotFileName)).size;
|
|
218
|
-
if (goldenSize === screenshotSize && screenshotFileBuffer.equals(goldenFileBuffer)) {
|
|
219
|
-
const success = await tryMoveFile(screenshotFileName, passFileName);
|
|
220
|
-
if (!success) return { pass: false, message: 'Problem moving file to "pass" directory.' };
|
|
221
|
-
infoManager.set({
|
|
222
|
-
new: {
|
|
223
|
-
path: passFileName.substring(rootLength)
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
return { pass: true };
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const { diff, pixelsDiff } = getPixelsDiff(screenshotImage, goldenImage);
|
|
230
|
-
|
|
231
|
-
if (pixelsDiff !== 0) {
|
|
232
|
-
infoManager.set({
|
|
233
|
-
diff: `${screenshotFile.substring(rootLength)}-diff.png`,
|
|
234
|
-
pixelsDiff
|
|
235
|
-
});
|
|
236
|
-
await writeFile(`${screenshotFile}-diff.png`, PNG.sync.write(diff));
|
|
237
|
-
return { pass: false, message: `Image does not match golden. ${pixelsDiff} pixels are different.` };
|
|
238
|
-
} else {
|
|
239
|
-
infoManager.set({
|
|
240
|
-
golden: {
|
|
241
|
-
byteSize: goldenSize
|
|
242
|
-
},
|
|
243
|
-
new: {
|
|
244
|
-
byteSize: screenshotSize
|
|
245
|
-
},
|
|
246
|
-
pixelsDiff
|
|
247
|
-
});
|
|
248
|
-
return { pass: false, message: 'Image diff is clean but the images do not have the same bytes.' };
|
|
249
|
-
}
|
|
250
|
-
} else {
|
|
251
|
-
return { resizeRequired: true };
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return await runCompareTest();
|
|
255
|
-
} else if (command === 'brightspace-visual-diff-compare-resize') {
|
|
256
|
-
const screenshotImage = PNG.sync.read(await readFile(screenshotFileName));
|
|
257
|
-
const goldenImage = PNG.sync.read(await readFile(goldenFileName));
|
|
258
|
-
|
|
259
|
-
const newWidth = Math.max(screenshotImage.width, goldenImage.width);
|
|
260
|
-
const newHeight = Math.max(screenshotImage.height, goldenImage.height);
|
|
261
|
-
const newSize = { width: newWidth, height: newHeight };
|
|
262
|
-
|
|
263
|
-
const newScreenshots = await createComparisonPNGs(screenshotImage, newSize);
|
|
264
|
-
const newGoldens = await createComparisonPNGs(goldenImage, newSize);
|
|
265
|
-
|
|
266
|
-
let bestIndex = -1;
|
|
267
|
-
let bestDiffImage = null;
|
|
268
|
-
let bestPixelsDiff = Number.MAX_SAFE_INTEGER;
|
|
269
|
-
for (let i = 0; i < newScreenshots.length; i++) {
|
|
270
|
-
const { diff, pixelsDiff } = getPixelsDiff(newScreenshots[i].png, newGoldens[i].png);
|
|
271
|
-
|
|
272
|
-
if (pixelsDiff < bestPixelsDiff) {
|
|
273
|
-
bestIndex = i;
|
|
274
|
-
bestDiffImage = diff;
|
|
275
|
-
bestPixelsDiff = pixelsDiff;
|
|
276
|
-
}
|
|
260
|
+
const goldenExists = await checkFileExists(goldenFileName);
|
|
261
|
+
if (!goldenExists) {
|
|
262
|
+
return { pass: false, message: 'No golden exists. Use the "--golden" CLI flag to re-run and re-generate goldens.' };
|
|
277
263
|
}
|
|
278
264
|
|
|
279
|
-
await
|
|
280
|
-
|
|
281
|
-
await writeFile(`${screenshotFile}-diff.png`, PNG.sync.write(bestDiffImage));
|
|
282
|
-
|
|
283
|
-
const rootLength = join(rootDir, PATHS.VDIFF_ROOT).length + 1;
|
|
265
|
+
const goldenFileBuffer = await readFile(goldenFileName);
|
|
266
|
+
const goldenImage = PNG.sync.read(goldenFileBuffer);
|
|
284
267
|
infoManager.set({
|
|
285
|
-
diff: `${screenshotFile.substring(rootLength)}-diff.png`,
|
|
286
268
|
golden: {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
269
|
+
height: goldenImage.height,
|
|
270
|
+
path: goldenFileName.substring(rootLength),
|
|
271
|
+
width: goldenImage.width
|
|
272
|
+
}
|
|
273
|
+
}, alt);
|
|
274
|
+
|
|
275
|
+
if (screenshotImage.width === goldenImage.width && screenshotImage.height === goldenImage.height) {
|
|
276
|
+
const goldenSize = (await stat(goldenFileName)).size;
|
|
277
|
+
const screenshotSize = (await stat(screenshotFileName)).size;
|
|
278
|
+
if (goldenSize === screenshotSize && screenshotFileBuffers[alt].equals(goldenFileBuffer)) {
|
|
279
|
+
const success = await tryMoveFile(screenshotFileName, passFileName);
|
|
280
|
+
if (!success) return { pass: false, message: 'Problem moving file to "pass" directory.' };
|
|
281
|
+
infoManager.set({
|
|
282
|
+
new: {
|
|
283
|
+
path: passFileName.substring(rootLength)
|
|
284
|
+
}
|
|
285
|
+
}, alt);
|
|
286
|
+
return { pass: true };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const { diff, pixelsDiff } = getPixelsDiff(screenshotImage, goldenImage);
|
|
290
|
+
|
|
291
|
+
if (pixelsDiff !== 0) {
|
|
292
|
+
infoManager.set({
|
|
293
|
+
diff: getFileName(`${screenshotFile}-diff`, alt).substring(rootLength),
|
|
294
|
+
pixelsDiff
|
|
295
|
+
}, alt);
|
|
296
|
+
await writeFile(getFileName(`${screenshotFile}-diff`, alt), PNG.sync.write(diff));
|
|
297
|
+
return { pass: false, message: `Image does not match golden. ${pixelsDiff} pixels are different.` };
|
|
298
|
+
} else {
|
|
299
|
+
infoManager.set({
|
|
300
|
+
golden: {
|
|
301
|
+
byteSize: goldenSize
|
|
302
|
+
},
|
|
303
|
+
new: {
|
|
304
|
+
byteSize: screenshotSize
|
|
305
|
+
},
|
|
306
|
+
pixelsDiff
|
|
307
|
+
}, alt);
|
|
308
|
+
return { pass: false, message: 'Image diff is clean but the images do not have the same bytes.' };
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
const { screenshot, golden, diff, pixelsDiff } = await getBestResizePng(screenshotImage, goldenImage);
|
|
312
|
+
|
|
313
|
+
const screenshotResizedPath = getFileName(`${screenshotFile}-resized-screenshot`, alt);
|
|
314
|
+
const goldenResizedPath = getFileName(`${screenshotFile}-resized-golden`, alt);
|
|
315
|
+
const diffPath = getFileName(`${screenshotFile}-diff`, alt);
|
|
316
|
+
await writeFile(screenshotResizedPath, PNG.sync.write(screenshot));
|
|
317
|
+
await writeFile(goldenResizedPath, PNG.sync.write(golden));
|
|
318
|
+
await writeFile(diffPath, PNG.sync.write(diff));
|
|
319
|
+
infoManager.set({
|
|
320
|
+
diff: diffPath.substring(rootLength),
|
|
321
|
+
golden: {
|
|
322
|
+
path: goldenResizedPath.substring(rootLength)
|
|
323
|
+
},
|
|
324
|
+
new: {
|
|
325
|
+
path: screenshotResizedPath.substring(rootLength)
|
|
326
|
+
},
|
|
327
|
+
pixelsDiff
|
|
328
|
+
}, alt);
|
|
294
329
|
|
|
295
|
-
|
|
330
|
+
return { pass: false, message: `Images are not the same size. When resized, ${pixelsDiff} pixels are different.` };
|
|
331
|
+
}
|
|
296
332
|
}
|
|
297
333
|
|
|
334
|
+
bestPosition = null; // reset best position
|
|
335
|
+
let pass = true;
|
|
336
|
+
let errors = '';
|
|
337
|
+
const testResults = {};
|
|
338
|
+
for (const test of tests) {
|
|
339
|
+
testResults[test] = await runCompareTest(test);
|
|
340
|
+
if (!testResults[test].pass) {
|
|
341
|
+
pass = false;
|
|
342
|
+
errors += `\n(${test}) ${testResults[test].message}`;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (pass) return { pass: true };
|
|
346
|
+
else if (tests.length === 1) return testResults[tests[0]];
|
|
347
|
+
else return { pass: false, message: `One or more tests failed:${errors}` };
|
|
298
348
|
}
|
|
299
349
|
};
|
|
300
350
|
}
|
|
@@ -85,39 +85,43 @@ function flattenResults(session, browserData, fileData) {
|
|
|
85
85
|
|
|
86
86
|
function collectTests(prefix, tests) {
|
|
87
87
|
tests.forEach(t => {
|
|
88
|
-
|
|
88
|
+
let testName = `${prefix}${t.name}`;
|
|
89
89
|
const testKey = testName.replaceAll(' > ', ' ');
|
|
90
|
-
const
|
|
90
|
+
const allTestInfo = getTestInfo(session, testKey);
|
|
91
91
|
|
|
92
92
|
// tests missing info but with no error were skipped via grep, so exclude them
|
|
93
|
-
if (!
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
93
|
+
if (!allTestInfo && !t.error) return;
|
|
94
|
+
|
|
95
|
+
for (const test in allTestInfo) {
|
|
96
|
+
const info = allTestInfo[test];
|
|
97
|
+
if (test !== 'default') testName = `${prefix}${t.name} (${test})`;
|
|
98
|
+
if (!fileData.tests.has(testName)) {
|
|
99
|
+
fileData.tests.set(testName, {
|
|
100
|
+
name: testName,
|
|
101
|
+
numByteDiff: 0,
|
|
102
|
+
numFailed: 0,
|
|
103
|
+
results: []
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const testData = fileData.tests.get(testName);
|
|
107
|
+
const bytediff = !t.passed && info?.diff === undefined && info?.pixelsDiff === 0;
|
|
108
|
+
if (!t.passed) {
|
|
109
|
+
if (bytediff) {
|
|
110
|
+
browserData.numByteDiff++;
|
|
111
|
+
testData.numByteDiff++;
|
|
112
|
+
}
|
|
113
|
+
browserData.numFailed++;
|
|
114
|
+
testData.numFailed++;
|
|
109
115
|
}
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
testData.results.push({
|
|
117
|
+
name: browserData.name,
|
|
118
|
+
duration: t.duration,
|
|
119
|
+
error: t.error?.message,
|
|
120
|
+
passed: t.passed,
|
|
121
|
+
bytediff,
|
|
122
|
+
info: info
|
|
123
|
+
});
|
|
112
124
|
}
|
|
113
|
-
testData.results.push({
|
|
114
|
-
name: browserData.name,
|
|
115
|
-
duration: t.duration,
|
|
116
|
-
error: t.error?.message,
|
|
117
|
-
passed: t.passed,
|
|
118
|
-
bytediff,
|
|
119
|
-
info: info
|
|
120
|
-
});
|
|
121
125
|
});
|
|
122
126
|
}
|
|
123
127
|
|