UNPKG

testplane

Version:

Tests framework based on mocha and wdio

160 lines 8.06 kB
"use strict"; 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