@testivai/witness-selenium 0.3.10 → 0.3.11
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 +1 -1
- package/dist/capture/full-page.d.ts +30 -0
- package/dist/capture/full-page.js +65 -0
- package/dist/witness.js +9 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ All of these stay `changed` unless you configure a pass criterion; `noiseAutoPas
|
|
|
57
57
|
|
|
58
58
|
## Full-page screenshots
|
|
59
59
|
|
|
60
|
-
- **Chrome / Edge** — true full-page capture via CDP (`captureBeyondViewport`), the same mechanism Playwright uses. Automatic; nothing to configure.
|
|
60
|
+
- **Chrome / Edge** — true full-page capture via CDP (`captureBeyondViewport`), the same mechanism Playwright uses. Automatic; nothing to configure. Chrome can return a frame painted for the wrong viewport while it sets up that capture, so the adapter waits for a freshly rendered frame, then captures until two captures in a row are identical (at most four). A stable page costs one extra screenshot; `TESTIVAI_DEBUG=true` logs when captures differed.
|
|
61
61
|
- **Firefox / Safari** — the Selenium JS bindings expose no full-page API, so captures are viewport-sized. Set the window to the size you want compared; runs are consistent either way. (The Python and Java TestivAI adapters do full-page on Firefox via its native API.)
|
|
62
62
|
|
|
63
63
|
## Stabilized captures
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CaptureLog } from '@testivai/witness';
|
|
2
|
+
import type { WitnessDriver } from '../types';
|
|
3
|
+
/** A Chromium driver: one that can send raw CDP commands. */
|
|
4
|
+
export type CdpDriver = Required<Pick<WitnessDriver, 'sendAndGetDevToolsCommand'>>;
|
|
5
|
+
/**
|
|
6
|
+
* Full-page screenshot through CDP: wait for a fresh frame, measure the
|
|
7
|
+
* content, then capture beyond the viewport until two captures in a row match
|
|
8
|
+
* (at most MAX_STABLE_CAPTURES from @testivai/witness).
|
|
9
|
+
*
|
|
10
|
+
* Chrome can hand back a frame painted with styles resolved against the wrong
|
|
11
|
+
* viewport while it sets up a beyond-viewport capture: narrow-viewport media
|
|
12
|
+
* rules, a collapsed 100vh hero, or em text drawn at a `max-width: 480px`
|
|
13
|
+
* body size. The page measures correctly but the pixels do not, so an
|
|
14
|
+
* unchanged page flips between "changed" and "passed" across runs. See
|
|
15
|
+
* capture/stable-capture.ts in @testivai/witness, which the standalone CLI
|
|
16
|
+
* and the Playwright adapter share.
|
|
17
|
+
*
|
|
18
|
+
* Selenium reaches the page only through `sendAndGetDevToolsCommand`, so the
|
|
19
|
+
* frame wait is CDP `Runtime.evaluate` with `awaitPromise`. Every command gets
|
|
20
|
+
* an explicit params object instead of relying on the bindings' default.
|
|
21
|
+
*
|
|
22
|
+
* A stable page costs one extra screenshot.
|
|
23
|
+
*
|
|
24
|
+
* @param driver A Chromium (Chrome/Edge) driver.
|
|
25
|
+
* @param log Optional debug sink.
|
|
26
|
+
* @returns The PNG bytes of the kept capture.
|
|
27
|
+
* @throws When the page reports no usable size or a capture returns no data,
|
|
28
|
+
* so the caller can fall back to a viewport screenshot.
|
|
29
|
+
*/
|
|
30
|
+
export declare function captureFullPageScreenshot(driver: CdpDriver, log?: CaptureLog): Promise<Buffer>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.captureFullPageScreenshot = captureFullPageScreenshot;
|
|
4
|
+
const witness_1 = require("@testivai/witness");
|
|
5
|
+
/**
|
|
6
|
+
* Full-page screenshot through CDP: wait for a fresh frame, measure the
|
|
7
|
+
* content, then capture beyond the viewport until two captures in a row match
|
|
8
|
+
* (at most MAX_STABLE_CAPTURES from @testivai/witness).
|
|
9
|
+
*
|
|
10
|
+
* Chrome can hand back a frame painted with styles resolved against the wrong
|
|
11
|
+
* viewport while it sets up a beyond-viewport capture: narrow-viewport media
|
|
12
|
+
* rules, a collapsed 100vh hero, or em text drawn at a `max-width: 480px`
|
|
13
|
+
* body size. The page measures correctly but the pixels do not, so an
|
|
14
|
+
* unchanged page flips between "changed" and "passed" across runs. See
|
|
15
|
+
* capture/stable-capture.ts in @testivai/witness, which the standalone CLI
|
|
16
|
+
* and the Playwright adapter share.
|
|
17
|
+
*
|
|
18
|
+
* Selenium reaches the page only through `sendAndGetDevToolsCommand`, so the
|
|
19
|
+
* frame wait is CDP `Runtime.evaluate` with `awaitPromise`. Every command gets
|
|
20
|
+
* an explicit params object instead of relying on the bindings' default.
|
|
21
|
+
*
|
|
22
|
+
* A stable page costs one extra screenshot.
|
|
23
|
+
*
|
|
24
|
+
* @param driver A Chromium (Chrome/Edge) driver.
|
|
25
|
+
* @param log Optional debug sink.
|
|
26
|
+
* @returns The PNG bytes of the kept capture.
|
|
27
|
+
* @throws When the page reports no usable size or a capture returns no data,
|
|
28
|
+
* so the caller can fall back to a viewport screenshot.
|
|
29
|
+
*/
|
|
30
|
+
async function captureFullPageScreenshot(driver, log) {
|
|
31
|
+
const waitOnce = async () => {
|
|
32
|
+
const evaluated = await driver.sendAndGetDevToolsCommand('Runtime.evaluate', {
|
|
33
|
+
expression: witness_1.FRESH_FRAME_EXPRESSION,
|
|
34
|
+
awaitPromise: true,
|
|
35
|
+
returnByValue: true,
|
|
36
|
+
});
|
|
37
|
+
return evaluated?.result?.value === true;
|
|
38
|
+
};
|
|
39
|
+
const settle = () => (0, witness_1.settleFreshFrame)(waitOnce, log);
|
|
40
|
+
await settle();
|
|
41
|
+
const metrics = await driver.sendAndGetDevToolsCommand('Page.getLayoutMetrics', {});
|
|
42
|
+
const size = metrics?.cssContentSize ?? metrics?.contentSize;
|
|
43
|
+
const width = Math.ceil(size?.width ?? 0);
|
|
44
|
+
const height = Math.ceil(size?.height ?? 0);
|
|
45
|
+
if (!(width > 0 && height > 0)) {
|
|
46
|
+
throw new Error(`page reported unusable content size ${width}x${height}`);
|
|
47
|
+
}
|
|
48
|
+
log?.(`page ${width}x${height}, viewport ${metrics.cssLayoutViewport?.clientWidth}x${metrics.cssLayoutViewport?.clientHeight}`);
|
|
49
|
+
const data = await (0, witness_1.captureUntilStable)({
|
|
50
|
+
capture: async () => {
|
|
51
|
+
const shot = await driver.sendAndGetDevToolsCommand('Page.captureScreenshot', {
|
|
52
|
+
format: 'png',
|
|
53
|
+
captureBeyondViewport: true,
|
|
54
|
+
clip: { x: 0, y: 0, width, height, scale: 1 },
|
|
55
|
+
});
|
|
56
|
+
if (typeof shot?.data !== 'string' || shot.data.length === 0) {
|
|
57
|
+
throw new Error('Page.captureScreenshot returned no data');
|
|
58
|
+
}
|
|
59
|
+
return shot.data;
|
|
60
|
+
},
|
|
61
|
+
settle,
|
|
62
|
+
log,
|
|
63
|
+
});
|
|
64
|
+
return Buffer.from(data, 'base64');
|
|
65
|
+
}
|
package/dist/witness.js
CHANGED
|
@@ -58,6 +58,7 @@ exports.witness = witness;
|
|
|
58
58
|
const witness_1 = require("@testivai/witness");
|
|
59
59
|
const fs = __importStar(require("fs"));
|
|
60
60
|
const path = __importStar(require("path"));
|
|
61
|
+
const full_page_1 = require("./capture/full-page");
|
|
61
62
|
/** id of the style element injected for the duration of a capture */
|
|
62
63
|
const STYLE_ID = '__testivai_capture_css__';
|
|
63
64
|
/**
|
|
@@ -158,13 +159,16 @@ async function waitForFonts(driver) {
|
|
|
158
159
|
async function captureScreenshot(driver) {
|
|
159
160
|
// Chromium drivers expose raw CDP; captureBeyondViewport gives a true
|
|
160
161
|
// full-page capture without resizing the window (which breaks 100vh
|
|
161
|
-
// layouts — the reason we never use the resize trick).
|
|
162
|
+
// layouts — the reason we never use the resize trick). It waits for a
|
|
163
|
+
// fresh frame and captures until two in a row match: Chrome can return a
|
|
164
|
+
// frame painted for the wrong viewport while it sets up the capture
|
|
165
|
+
// (capture/full-page.ts).
|
|
162
166
|
if (typeof driver.sendAndGetDevToolsCommand === 'function') {
|
|
167
|
+
const captureLog = process.env.TESTIVAI_DEBUG === 'true'
|
|
168
|
+
? (message) => console.log(`[TestivAI] Full-page capture: ${message}`)
|
|
169
|
+
: undefined;
|
|
163
170
|
try {
|
|
164
|
-
|
|
165
|
-
if (result && typeof result.data === 'string' && result.data.length > 0) {
|
|
166
|
-
return Buffer.from(result.data, 'base64');
|
|
167
|
-
}
|
|
171
|
+
return await (0, full_page_1.captureFullPageScreenshot)(driver, captureLog);
|
|
168
172
|
}
|
|
169
173
|
catch {
|
|
170
174
|
// fall through to the viewport screenshot
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testivai/witness-selenium",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "Selenium WebDriver adapter for TestivAI Witness — local-first visual regression with pixel + DOM comparison",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|