testplane
Version:
Tests framework based on mocha and wdio
160 lines • 8.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FullPageScreenShooter = void 0;
const composite_image_1 = require("./composite-image");
const types_1 = require("../isomorphic/types");
const client_bridge_1 = require("../client-bridge");
const types_2 = require("../isomorphic/types");
const operations_1 = require("./operations");
const history_1 = require("../history");
const constants_1 = require("./constants");
const debug_1 = require("./debug");
const debug = (0, debug_1.makeVerboseScreenshotsDebug)("testplane:screenshots:full-page-screen-shooter");
class FullPageScreenShooter {
static async create(params) {
const browserSideScreenshooter = await client_bridge_1.ClientBridge.create(params.browser, "screen-shooter", { needsCompatLib: params.browserProperties.needsCompatLib });
return new this({ ...params, browserSideScreenshooter });
}
constructor({ browser, camera, browserProperties, browserSideScreenshooter }) {
this._browser = browser;
this._camera = camera;
this._browserProperties = browserProperties;
this._browserSideScreenshooter = browserSideScreenshooter;
}
async capture(opts = {}) {
if (!opts.disableHover) {
opts.disableHover = types_1.DisableHoverMode.WhenScrollingNeeded;
}
opts.disableAnimation ??= true;
try {
return await this._captureImpl(opts);
}
finally {
try {
await this._cleanup(opts);
}
catch (cleanupError) {
const cleanupMessage = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
console.warn(`Warning: failed to cleanup after full page screenshot.\nCleanup error: ${cleanupMessage}`);
}
}
}
async _captureImpl(opts) {
const prepareResult = await this._browserSideScreenshooter.call("prepareFullPageScreenshot", [
{
usePixelRatio: this._browserProperties.shouldUsePixelRatio,
disableAnimation: opts.disableAnimation,
disableHover: opts.disableHover,
},
]);
if ((0, types_2.isBrowserSideError)(prepareResult)) {
throw new Error(`Failed to prepare full page screenshot: error '${prepareResult.errorCode}': ${prepareResult.message}`);
}
// https://github.com/webdriverio/webdriverio/issues/11396
if (this._browserProperties.isWebdriverProtocol && opts.disableAnimation) {
await (0, operations_1.disableIframeAnimations)(this._browser, this._browserSideScreenshooter);
}
await (0, operations_1.preparePointerForScreenshot)(this._browser, {
disableHover: opts.disableHover,
pointerEventsDisabled: prepareResult.pointerEventsDisabled,
});
const { documentSize, viewportSize, safeArea } = prepareResult;
let lastElementPositionsProbe = prepareResult.elementPositionsProbe;
let { viewportOffset } = prepareResult;
debug("Prepared full page screenshot.\n documentSize: %O\n viewportSize: %O\n viewportOffset: %O\n safeArea: %O", documentSize, viewportSize, viewportOffset, safeArea);
const fullPageRect = this._buildFullPageRect(documentSize, viewportOffset);
const viewportImage = await this._camera.captureViewportImage({
viewportSize,
viewportOffset,
screenshotDelay: opts.screenshotDelay,
cropMargins: opts.cropMargins,
});
const compositeImage = composite_image_1.CompositeImage.create();
await compositeImage.registerViewportImageAtOffset(viewportImage, safeArea, [{ full: fullPageRect, clip: fullPageRect, visible: fullPageRect }], []);
let hasCapturedWholePage = viewportOffset.top + viewportSize.height >= documentSize.height;
let iterations = 0;
while (!hasCapturedWholePage && iterations < constants_1.COMPOSITING_ITERATIONS_LIMIT) {
const scrollResult = await this._browserSideScreenshooter.call("scrollFullPage", [
safeArea.height,
{ usePixelRatio: this._browserProperties.shouldUsePixelRatio },
]);
if ((0, types_2.isBrowserSideError)(scrollResult)) {
throw new Error(`Failed to scroll during full page capture: error '${scrollResult.errorCode}': ${scrollResult.message}`);
}
const hasReachedScrollLimit = scrollResult.viewportOffset.top === viewportOffset.top &&
scrollResult.viewportOffset.left === viewportOffset.left;
if (hasReachedScrollLimit) {
debug("Reached scroll limit at viewportOffset: %O", viewportOffset);
break;
}
const hasElementPositionsChanged = !this._isElementPositionsProbeEqual(lastElementPositionsProbe, scrollResult.elementPositionsProbe);
if (!hasElementPositionsChanged) {
debug("Element positions probe did not change after scroll; falling back to initial viewport screenshot");
return viewportImage;
}
lastElementPositionsProbe = scrollResult.elementPositionsProbe;
viewportOffset = scrollResult.viewportOffset;
const updatedFullPageRect = this._buildFullPageRect(documentSize, viewportOffset);
const chunkImage = await this._camera.captureViewportImage({
viewportSize,
viewportOffset,
screenshotDelay: opts.screenshotDelay,
cropMargins: opts.cropMargins,
});
await compositeImage.registerViewportImageAtOffset(chunkImage, safeArea, [{ full: updatedFullPageRect, clip: updatedFullPageRect, visible: updatedFullPageRect }], []);
hasCapturedWholePage =
viewportOffset.top + viewportSize.height >= documentSize.height;
iterations++;
}
debug("Full page capture complete. Iterations: %d, captured whole page: %s", iterations, hasCapturedWholePage);
return compositeImage.render();
}
_isElementPositionsProbeEqual(leftProbes, rightProbes) {
if (leftProbes.length !== rightProbes.length) {
return false;
}
for (let i = 0; i < leftProbes.length; i++) {
const left = leftProbes[i];
const right = rightProbes[i];
if (left === null || right === null) {
if (left !== right) {
return false;
}
continue;
}
if (left.left !== right.left ||
left.top !== right.top ||
left.width !== right.width ||
left.height !== right.height) {
return false;
}
}
return true;
}
async _cleanup(opts) {
return (0, history_1.runWithoutHistory)({}, async () => {
await (0, operations_1.cleanupScrolls)(this._browserSideScreenshooter);
if (opts.disableAnimation) {
await (0, operations_1.cleanupPageAnimations)(this._browser, this._browserSideScreenshooter, this._browserProperties.isWebdriverProtocol);
}
if (opts.disableHover && opts.disableHover !== "never") {
await (0, operations_1.cleanupPointerEvents)(this._browserSideScreenshooter);
}
});
}
/**
* Builds the full-page rect in viewport coordinates.
* The rect always has the full document dimensions, but its top shifts
* upward as we scroll (the document "moves up" relative to the viewport).
*/
_buildFullPageRect(documentSize, viewportOffset) {
return {
left: -viewportOffset.left,
top: -viewportOffset.top,
width: documentSize.width,
height: documentSize.height,
};
}
}
exports.FullPageScreenShooter = FullPageScreenShooter;
//# sourceMappingURL=full-page-screen-shooter.js.map