Skip to content

Commit 9afdc37

Browse files
authored
feat: improve and refactor screen-shooter and split it into 3 versions (#1240)
1 parent 330d518 commit 9afdc37

29 files changed

Lines changed: 2328 additions & 1754 deletions

src/browser/commands/assert-view/index.js

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const path = require("path");
55
const _ = require("lodash");
66
const { pngValidator: validatePng } = require("png-validator");
77
const { Image } = require("../../../image");
8-
const { ScreenShooter } = require("../../screen-shooter");
8+
const { ElementsScreenShooter } = require("../../screen-shooter/elements-screen-shooter");
9+
const { ViewportScreenShooter } = require("../../screen-shooter/viewport-screen-shooter");
910
const temp = require("../../../temp");
1011
const { getCaptureProcessors } = require("./capture-processors");
1112
const RuntimeConfig = require("../../../config/runtime-config");
@@ -31,7 +32,21 @@ const getIgnoreDiffPixelCountRatio = value => {
3132
};
3233

3334
module.exports.default = browser => {
34-
const screenShooter = ScreenShooter.create(browser);
35+
const browserProperties = {
36+
isWebdriverProtocol: browser.isWebdriverProtocol,
37+
shouldUsePixelRatio: browser.shouldUsePixelRatio,
38+
needsCompatLib: browser.needsCompatLib,
39+
};
40+
const screenShooterPromise = ElementsScreenShooter.create({
41+
camera: browser.camera,
42+
browser: browser.publicAPI,
43+
browserProperties,
44+
});
45+
const viewportScreenShooterPromise = ViewportScreenShooter.create({
46+
camera: browser.camera,
47+
browser: browser.publicAPI,
48+
browserProperties,
49+
});
3550
const { publicAPI: session, config } = browser;
3651
const {
3752
assertViewOpts,
@@ -45,28 +60,20 @@ module.exports.default = browser => {
4560

4661
const { handleNoRefImage, handleImageDiff, handleInvalidRefImage } = getCaptureProcessors();
4762

48-
const assertView = async (state, selectors, opts) => {
49-
opts = _.defaults(opts, assertViewOpts, {
63+
const getDefaultOpts = opts =>
64+
_.defaults(opts, assertViewOpts, {
5065
compositeImage,
5166
screenshotDelay,
5267
tolerance,
5368
antialiasingTolerance,
5469
disableAnimation,
5570
});
5671

72+
const compareScreenshot = async (state, currImgInst, currImgMeta, opts) => {
5773
const { testplaneCtx } = session.executionContext;
5874
const test = session.executionContext.ctx.currentTest;
5975
testplaneCtx.assertViewResults = testplaneCtx.assertViewResults || AssertViewResults.create();
6076

61-
let debugId = "debugId";
62-
try {
63-
debugId = `${test.fullTitle()}.${browser.id}.${state}`;
64-
} catch {
65-
/**/
66-
}
67-
debug(`[${debugId}] assertView selectors: %O`, selectors);
68-
debug(`[${debugId}] assertView opts: %O`, opts);
69-
7077
if (testplaneCtx.assertViewResults.hasState(state)) {
7178
return Promise.reject(new AssertViewError(`duplicate name for "${state}" state`));
7279
}
@@ -77,7 +84,6 @@ module.exports.default = browser => {
7784
const { tempOpts } = RuntimeConfig.getInstance();
7885
temp.attach(tempOpts);
7986

80-
const { image: currImgInst, meta: currImgMeta } = await screenShooter.capture(selectors, opts);
8187
const currSize = await currImgInst.getSize();
8288
const currImg = { path: temp.path(Object.assign(tempOpts, { suffix: ".png" })), size: currSize };
8389

@@ -153,11 +159,44 @@ module.exports.default = browser => {
153159
testplaneCtx.assertViewResults.add({ stateName: state, refImg: refImg });
154160
};
155161

162+
const assertView = async (state, selectors, opts) => {
163+
opts = getDefaultOpts(opts);
164+
165+
let debugId = "debugId";
166+
try {
167+
const test = session.executionContext.ctx.currentTest;
168+
debugId = `${test.fullTitle()}.${browser.id}.${state}`;
169+
opts.debugId = debugId;
170+
} catch {
171+
/**/
172+
}
173+
debug(`[${debugId}] assertView selectors: %O`, selectors);
174+
debug(`[${debugId}] assertView opts: %O`, opts);
175+
176+
const screenShooter = await screenShooterPromise;
177+
const { image, meta } = await screenShooter.capture(selectors, opts);
178+
179+
return compareScreenshot(state, image, meta, opts);
180+
};
181+
182+
const PSEUDO_SELECTOR_REGEXP = /(.*?)(::before|::after)\s*$/i;
183+
const getSelectorToWaitForExist = selector => {
184+
if (!_.isString(selector)) {
185+
return selector;
186+
}
187+
const match = selector.match(PSEUDO_SELECTOR_REGEXP);
188+
if (!match) {
189+
return selector;
190+
}
191+
const elementSelector = match[1].trim();
192+
return elementSelector || selector;
193+
};
194+
156195
const waitSelectorsForExist = async (browser, selectors) => {
157196
await Promise.all(
158197
[].concat(selectors).map(selector =>
159198
browser
160-
.$(selector)
199+
.$(getSelectorToWaitForExist(selector))
161200
.then(el => el.waitForExist())
162201
.catch(() => {
163202
throw new Error(
@@ -175,13 +214,14 @@ module.exports.default = browser => {
175214
};
176215

177216
const assertViewByViewport = async (state, opts) => {
178-
opts = Object.assign(opts, {
179-
allowViewportOverflow: true,
180-
compositeImage: false,
181-
captureElementFromTop: false,
182-
});
217+
opts = getDefaultOpts(opts);
218+
219+
debug(`assertViewByViewport state: ${state}, opts: %O`, opts);
220+
221+
const vpScreenShooter = await viewportScreenShooterPromise;
222+
const { image, meta } = await vpScreenShooter.capture(opts);
183223

184-
return assertView(state, "body", opts);
224+
return compareScreenshot(state, image, meta, opts);
185225
};
186226

187227
const shouldAssertViewport = selectorsOrOpts => {

src/browser/existing-browser.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { attach, type AttachOptions, type ElementArray } from "@testplane/webdri
44
import { sessionEnvironmentDetector } from "@testplane/wdio-utils";
55
import { Browser, BrowserOpts } from "./browser";
66
import { customCommandFileNames } from "./commands";
7-
import { Camera, ImageArea } from "./camera";
8-
import { type ClientBridge, build as buildClientBridge } from "./client-bridge";
7+
import { Camera, CaptureViewportImageOpts } from "./camera";
8+
import { ClientBridge } from "./client-bridge";
99
import * as history from "./history";
1010
import * as logger from "../utils/logger";
1111
import { WEBDRIVER_PROTOCOL } from "../constants/config";
@@ -19,6 +19,7 @@ import { NEW_ISSUE_LINK } from "../constants/help";
1919
import type { SessionOptions } from "./types";
2020
import { Page } from "puppeteer-core";
2121
import { CDP } from "./cdp";
22+
import type * as browserSideUtilsImplementation from "./client-scripts/browser-utils/implementation";
2223

2324
const OPTIONAL_SESSION_OPTS = ["transformRequest", "transformResponse"];
2425

@@ -30,7 +31,6 @@ interface ScrollByParams {
3031

3132
const BROWSER_SESSION_HINT = "browser session";
3233
const CDP_CONNECTION_HINT = "cdp connection";
33-
const CLIENT_BRIDGE_HINT = "client bridge";
3434

3535
function ensure<T>(value: T | undefined | null, hint?: string): asserts value is T {
3636
if (!value) {
@@ -73,7 +73,7 @@ export class ExistingBrowser extends Browser {
7373
protected _camera: Camera;
7474
protected _meta: Record<string, unknown>;
7575
protected _calibration?: CalibrationResult;
76-
protected _clientBridge?: ClientBridge;
76+
protected _browserSideUtils?: ClientBridge<typeof browserSideUtilsImplementation>;
7777
protected _cdp: CDP | null = null;
7878
protected _tags: Set<string> = new Set();
7979

@@ -125,7 +125,7 @@ export class ExistingBrowser extends Browser {
125125

126126
await this._prepareSession();
127127
await this._performCalibration(calibrator);
128-
await this._buildClientScripts();
128+
await this._initBrowserSideUtils();
129129
},
130130
);
131131

@@ -167,11 +167,6 @@ export class ExistingBrowser extends Browser {
167167
return this._session.execute(script);
168168
}
169169

170-
callMethodOnBrowserSide<T>(name: string, args: unknown[] = []): Promise<T> {
171-
ensure(this._clientBridge, CLIENT_BRIDGE_HINT);
172-
return this._clientBridge.call(name, args);
173-
}
174-
175170
get shouldUsePixelRatio(): boolean {
176171
return this._calibration ? this._calibration.usePixelRatio : true;
177172
}
@@ -180,6 +175,10 @@ export class ExistingBrowser extends Browser {
180175
return this._config.automationProtocol === WEBDRIVER_PROTOCOL;
181176
}
182177

178+
get needsCompatLib(): boolean {
179+
return this._calibration ? this._calibration.needsCompatLib : false;
180+
}
181+
183182
async captureViewportImage(opts?: CaptureViewportImageOpts, screenshotDelay?: number): Promise<Image> {
184183
if (screenshotDelay) {
185184
await new Promise(resolve => setTimeout(resolve, screenshotDelay));
@@ -327,8 +326,8 @@ export class ExistingBrowser extends Browser {
327326
this.restoreHttpTimeout();
328327
}
329328

330-
if (this._clientBridge) {
331-
await this._clientBridge.call("resetZoom");
329+
if (this._browserSideUtils) {
330+
await this._browserSideUtils.call("resetZoom", []);
332331
}
333332

334333
return result;
@@ -473,10 +472,10 @@ export class ExistingBrowser extends Browser {
473472
});
474473
}
475474

476-
protected async _buildClientScripts(): Promise<ClientBridge> {
477-
return buildClientBridge(this, { calibration: this._calibration }).then(
478-
clientBridge => (this._clientBridge = clientBridge),
479-
);
475+
protected async _initBrowserSideUtils(): Promise<ClientBridge<typeof browserSideUtilsImplementation>> {
476+
return ClientBridge.create(this._session!, "browser-utils", {
477+
needsCompatLib: this._calibration?.needsCompatLib,
478+
}).then(clientBridge => (this._browserSideUtils = clientBridge));
480479
}
481480

482481
_stubCommands(): void {

src/browser/screen-shooter/composite-image/debug-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { convertRgbaToPng } from "../../../utils/eight-bit-rgba-to-png";
44
import { loadEsm } from "../../../utils/preload-utils";
55
import type { Coord, Rect, Size, YBand } from "../../isomorphic/geometry";
66
import path from "node:path";
7-
import { CaptureSpec } from "../../client-scripts/screen-shooter/types";
7+
import type { CaptureSpec } from "../../client-scripts/screen-shooter/types";
88

99
type DebugRectColor = { r: number; g: number; b: number; a: number };
1010

0 commit comments

Comments
 (0)