testaro 77.2.0 → 78.0.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/README.md +4 -1
- package/package.json +1 -1
- package/procs/catalog.js +35 -3
- package/procs/launch.js +14 -5
- package/procs/shoot.js +9 -4
package/README.md
CHANGED
|
@@ -142,6 +142,7 @@ Here is a sample job, showing properties that you can set:
|
|
|
142
142
|
strict: true, // Whether to reject redirections from the target URL
|
|
143
143
|
standard: 'only', // Report native (no), standard (only), or both (also) results
|
|
144
144
|
imageColor: 0, // Color type (0, 2, 4, 6) of the page image, if one is to be created along with a catalog
|
|
145
|
+
imageScale: 2, // Optional: also capture the page image at this device pixel density (see the images section)
|
|
145
146
|
device: { // Device to emulate
|
|
146
147
|
id: 'iPhone 8',
|
|
147
148
|
windowOptions: {
|
|
@@ -375,7 +376,9 @@ In some cases no catalog entry can be found. The reasons may include:
|
|
|
375
376
|
|
|
376
377
|
#### `images`
|
|
377
378
|
|
|
378
|
-
Testaro inserts an `images` array property if necessary to store page images in the report. If the job has an `imageColor` property with `0`, `2`, `4`, or `6` as its value and Testaro will insert a `catalog` property, then Testaro also creates a page image with that color type and makes its base64-encoded PNG the first item in the `images` array.
|
|
379
|
+
Testaro inserts an `images` array property if necessary to store page images in the report. If the job has an `imageColor` property with `0`, `2`, `4`, or `6` as its value and Testaro will insert a `catalog` property, then Testaro also creates a page image with that color type and makes its base64-encoded PNG the first item in the `images` array. The first item is always captured at CSS-pixel scale (one image pixel per CSS pixel), so the `motion` test of the `testaro` tool can compare it with its own CSS-pixel screenshot.
|
|
380
|
+
|
|
381
|
+
If the job also has an `imageScale` property with a number greater than 1 as its value, then the catalog page is rendered at that device scale factor, and Testaro captures a second page image at device-pixel scale and makes it the second item in the `images` array. That image has `imageScale` times the pixels of the CSS layout in each dimension, for crisp display on high-resolution screens. The `boxID` properties of the catalog remain in CSS pixels; consumers can map them onto the second image by multiplying the coordinates by `imageScale`. Fractional values (such as a device's native `2.625`) are valid. A natural choice is the emulated device's own `deviceScaleFactor`, which also makes the catalog page select the same `srcset`/`image-set` resources as the test pages. If `imageScale` is omitted, `1`, or invalid, the behavior is identical to that before this property existed.
|
|
379
382
|
|
|
380
383
|
There is a `shoot` act type that can be used to make additional page images during a job.
|
|
381
384
|
|
package/package.json
CHANGED
package/procs/catalog.js
CHANGED
|
@@ -33,6 +33,16 @@ const {shoot} = require('./shoot');
|
|
|
33
33
|
exports.getCatalog = async report => {
|
|
34
34
|
const {browserID} = report;
|
|
35
35
|
const targetURL = report.target?.url;
|
|
36
|
+
// Image scale factor (report.imageScale). When greater than 1, the catalog context
|
|
37
|
+
// runs at that deviceScaleFactor and a supplemental page image is captured at device
|
|
38
|
+
// scale, i.e. with imageScale times the pixels of the CSS layout. Box IDs are
|
|
39
|
+
// CSS-pixel regardless, because getBoundingClientRect reports CSS pixels by
|
|
40
|
+
// definition; consumers map them onto the supplemental image by multiplying by
|
|
41
|
+
// imageScale. Omitted, 1, or invalid values keep the behavior identical to before
|
|
42
|
+
// this option existed.
|
|
43
|
+
const imageScale = Number.isFinite(report.imageScale) && report.imageScale > 1
|
|
44
|
+
? report.imageScale
|
|
45
|
+
: 1;
|
|
36
46
|
// If the report specifies a global browser ID and a global target URL:
|
|
37
47
|
if (browserID && targetURL) {
|
|
38
48
|
// Launch a browser and visit the target, or abort the job on failure.
|
|
@@ -40,7 +50,8 @@ exports.getCatalog = async report => {
|
|
|
40
50
|
report,
|
|
41
51
|
actIndex: null,
|
|
42
52
|
tempBrowserID: browserID,
|
|
43
|
-
tempURL: targetURL
|
|
53
|
+
tempURL: targetURL,
|
|
54
|
+
contextOverrides: imageScale > 1 ? {deviceScaleFactor: imageScale} : {}
|
|
44
55
|
});
|
|
45
56
|
// If the launch and navigation succeeded:
|
|
46
57
|
if (page) {
|
|
@@ -58,13 +69,27 @@ exports.getCatalog = async report => {
|
|
|
58
69
|
});
|
|
59
70
|
// If a page image is required:
|
|
60
71
|
if ([0, 2, 4, 6].includes(report.imageColor)) {
|
|
61
|
-
// Create one and add it to the report.
|
|
72
|
+
// Create one at CSS-pixel scale and add it to the report as images[0]. This
|
|
73
|
+
// scale is invariant to imageScale and to the context's deviceScaleFactor, so
|
|
74
|
+
// the testaro motion rule, which compares its own CSS-scale screenshot with
|
|
75
|
+
// images[0], is unaffected by the imageScale option.
|
|
62
76
|
console.log('Creating page image');
|
|
63
77
|
await shoot(page, report, {
|
|
64
78
|
exclusionSelector: '',
|
|
65
79
|
colorType: report.imageColor,
|
|
66
80
|
action: 'report'
|
|
67
81
|
});
|
|
82
|
+
// If a supersampled page image is also required:
|
|
83
|
+
if (imageScale > 1) {
|
|
84
|
+
// Create one at device-pixel scale and add it to the report as images[1].
|
|
85
|
+
console.log(`Creating page image at ${imageScale}x device scale`);
|
|
86
|
+
await shoot(page, report, {
|
|
87
|
+
exclusionSelector: '',
|
|
88
|
+
colorType: report.imageColor,
|
|
89
|
+
action: 'report',
|
|
90
|
+
scale: 'device'
|
|
91
|
+
});
|
|
92
|
+
}
|
|
68
93
|
}
|
|
69
94
|
// Get a catalog of the elements in the page and a map of path IDs to catalog indexes.
|
|
70
95
|
console.log('Creating catalog');
|
|
@@ -114,7 +139,14 @@ exports.getCatalog = async report => {
|
|
|
114
139
|
texts[text] ??= [];
|
|
115
140
|
texts[text].push(index);
|
|
116
141
|
}
|
|
117
|
-
|
|
142
|
+
// Get its bounding box, but only if the element is painted. Chromium reports
|
|
143
|
+
// plausible nonzero boxes for laid-out but unpainted content (visibility: hidden
|
|
144
|
+
// and content-visibility: hidden subtrees), and such a box disagrees with the
|
|
145
|
+
// page image, overlapping unrelated visible elements.
|
|
146
|
+
const isVisible = typeof element.checkVisibility === 'function'
|
|
147
|
+
? element.checkVisibility({checkVisibilityCSS: true, visibilityProperty: true})
|
|
148
|
+
: true;
|
|
149
|
+
const domRect = isVisible ? element.getBoundingClientRect() : null;
|
|
118
150
|
// Get its box ID.
|
|
119
151
|
const boxID = domRect
|
|
120
152
|
? ['x', 'y', 'width', 'height'].map(key => Math.round(domRect[key])).join(':')
|
package/procs/launch.js
CHANGED
|
@@ -281,7 +281,10 @@ const launchOnce = async opts => {
|
|
|
281
281
|
tempURL = '',
|
|
282
282
|
headEmulation = 'high',// low, high
|
|
283
283
|
xPathNeed = 'script',// own, script, attribute, none
|
|
284
|
-
needsAccessibleName = false
|
|
284
|
+
needsAccessibleName = false,
|
|
285
|
+
// Extra Playwright context options (e.g. deviceScaleFactor for device-pixel page
|
|
286
|
+
// images), spread last so they win over the defaults.
|
|
287
|
+
contextOverrides = {}
|
|
285
288
|
} = opts;
|
|
286
289
|
const act = report.acts[actIndex] ?? {};
|
|
287
290
|
const {device} = report;
|
|
@@ -373,7 +376,9 @@ const launchOnce = async opts => {
|
|
|
373
376
|
'Accept-Encoding': 'gzip, deflate, br',
|
|
374
377
|
'DNT': '1',
|
|
375
378
|
'Upgrade-Insecure-Requests': '1'
|
|
376
|
-
}
|
|
379
|
+
},
|
|
380
|
+
// Caller-specified context options (see contextOverrides above).
|
|
381
|
+
...contextOverrides
|
|
377
382
|
};
|
|
378
383
|
browserContext = await browser.newContext(contextOptions);
|
|
379
384
|
// Prevent default timeouts.
|
|
@@ -599,7 +604,9 @@ exports.launch = async (opts = {}) => {
|
|
|
599
604
|
headEmulation = 'high',
|
|
600
605
|
xPathNeed = 'script',
|
|
601
606
|
needsAccessibleName = false,
|
|
602
|
-
retries = 2
|
|
607
|
+
retries = 2,
|
|
608
|
+
// Extra Playwright context options, passed through to launchOnce.
|
|
609
|
+
contextOverrides = {}
|
|
603
610
|
} = opts;
|
|
604
611
|
// If the report is valid:
|
|
605
612
|
const jobValidation = isValidJob(report);
|
|
@@ -615,7 +622,8 @@ exports.launch = async (opts = {}) => {
|
|
|
615
622
|
tempURL,
|
|
616
623
|
headEmulation,
|
|
617
624
|
xPathNeed,
|
|
618
|
-
needsAccessibleName
|
|
625
|
+
needsAccessibleName,
|
|
626
|
+
contextOverrides
|
|
619
627
|
}
|
|
620
628
|
);
|
|
621
629
|
// If the launch and navigation succeeded:
|
|
@@ -657,7 +665,8 @@ exports.launch = async (opts = {}) => {
|
|
|
657
665
|
tempURL,
|
|
658
666
|
headEmulation,
|
|
659
667
|
xPathNeed,
|
|
660
|
-
needsAccessibleName
|
|
668
|
+
needsAccessibleName,
|
|
669
|
+
contextOverrides
|
|
661
670
|
}
|
|
662
671
|
);
|
|
663
672
|
// If the launch and navigation succeeded:
|
package/procs/shoot.js
CHANGED
|
@@ -26,10 +26,13 @@ const randomFileName = (suffixLength = 3) => {
|
|
|
26
26
|
return fileName;
|
|
27
27
|
};
|
|
28
28
|
// Creates and returns a screenshot.
|
|
29
|
-
const screenShot = async (page, exclusionLocator = null) => {
|
|
29
|
+
const screenShot = async (page, exclusionLocator = null, scale = 'css') => {
|
|
30
30
|
const options = {
|
|
31
31
|
fullPage: true,
|
|
32
|
-
|
|
32
|
+
// 'css' renders 1 image pixel per CSS pixel; 'device' renders at the context's
|
|
33
|
+
// deviceScaleFactor, i.e. an image whose pixel coordinates are the CSS
|
|
34
|
+
// coordinates multiplied by that factor.
|
|
35
|
+
scale,
|
|
33
36
|
timeout: applyMultiplier(4000)
|
|
34
37
|
};
|
|
35
38
|
if (exclusionLocator) {
|
|
@@ -49,10 +52,12 @@ exports.shoot = async (page, report, {
|
|
|
49
52
|
// Color fidelity: 0 (grayscale), 2 (RGB), 4 (grayscale alpha), 6 (RGBA).
|
|
50
53
|
colorType = 0,
|
|
51
54
|
// Disposition: return, report, file.
|
|
52
|
-
action = 'return'
|
|
55
|
+
action = 'return',
|
|
56
|
+
// Screenshot scale ('css' or 'device'); see screenShot above.
|
|
57
|
+
scale = 'css'
|
|
53
58
|
} = {}) => {
|
|
54
59
|
// Make and get a screenshot as a buffer.
|
|
55
|
-
let shot = await screenShot(page, exclusionSelector ? page.locator(exclusionSelector) : null);
|
|
60
|
+
let shot = await screenShot(page, exclusionSelector ? page.locator(exclusionSelector) : null, scale);
|
|
56
61
|
// If it succeeded:
|
|
57
62
|
if (shot.length) {
|
|
58
63
|
// Get the screenshot as an object representation of a PNG image.
|