UNPKG

@saucelabs/cypress-visual-plugin

Version:

Cypress plugin for Sauce Visual Testing

915 lines (904 loc) 28.4 kB
import { __async, __spreadProps, __spreadValues } from "./chunk-CIGAQ47A.js"; // src/index.ts import { BuildMode, DiffingMethod, ensureError, getApi, OperatingSystem, selectiveRegionOptionsToDiffingOptions, VisualApiRegion, getVisualResults } from "@saucelabs/visual"; // node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; var styles = { modifier: { reset: [0, 0], // 21 isn't widely supported and 22 does the same thing bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], // Bright color blackBright: [90, 39], gray: [90, 39], // Alias of `blackBright` grey: [90, 39], // Alias of `blackBright` redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], // Bright color bgBlackBright: [100, 49], bgGray: [100, 49], // Alias of `bgBlackBright` bgGrey: [100, 49], // Alias of `bgBlackBright` bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; var modifierNames = Object.keys(styles.modifier); var foregroundColorNames = Object.keys(styles.color); var backgroundColorNames = Object.keys(styles.bgColor); var colorNames = [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\x1B[${style[0]}m`, close: `\x1B[${style[1]}m` }; group[styleName] = styles[styleName]; codes.set(style[0], style[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles, "codes", { value: codes, enumerable: false }); styles.color.close = "\x1B[39m"; styles.bgColor.close = "\x1B[49m"; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value(hex) { const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ /* eslint-disable no-bitwise */ integer >> 16 & 255, integer >> 8 & 255, integer & 255 /* eslint-enable no-bitwise */ ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false } }); return styles; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; // node_modules/chalk/source/vendor/supports-color/index.js import process2 from "process"; import os from "os"; import tty from "tty"; function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = process2; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (process2.platform === "win32") { const osRelease = os.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if ("GITHUB_ACTIONS" in env || "GITEA_ACTIONS" in env) { return 3; } if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === "truecolor") { return 3; } if (env.TERM === "xterm-kitty") { return 3; } if ("TERM_PROGRAM" in env) { const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { return version >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; } } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, __spreadValues({ streamIsTTY: stream && stream.isTTY }, options)); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: tty.isatty(1) }), stderr: createSupportsColor({ isTTY: tty.isatty(2) }) }; var supports_color_default = supportsColor; // node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.slice(endIndex, index) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles2 = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style] of Object.entries(ansi_styles_default)) { styles2[styleName] = { get() { const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles2.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles2[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles2[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, __spreadProps(__spreadValues({}, styles2), { level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } })); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self, string) => { if (self.level <= 0 || !string) { return self[IS_EMPTY] ? "" : string; } let styler = self[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles2); var chalk = createChalk(); var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // src/messages.ts function buildUrl(url, options = { reviewReady: false }) { const extraLine = options.reviewReady ? ` ${source_default.grey(source_default.italic("You can now review your changes"))} ` : ""; return ` ${source_default.grey("".padStart(100, "-"))} ${source_default.underline(source_default.bold(source_default.yellow("Sauce Labs Visual:")))} ${extraLine} ${source_default.white(url.padStart(100, " "))} `; } // ../node_modules/macos-release/index.js import os2 from "os"; var nameMap = /* @__PURE__ */ new Map([ [24, ["Sequoia", "15"]], [23, ["Sonoma", "14"]], [22, ["Ventura", "13"]], [21, ["Monterey", "12"]], [20, ["Big Sur", "11"]], [19, ["Catalina", "10.15"]], [18, ["Mojave", "10.14"]], [17, ["High Sierra", "10.13"]], [16, ["Sierra", "10.12"]], [15, ["El Capitan", "10.11"]], [14, ["Yosemite", "10.10"]], [13, ["Mavericks", "10.9"]], [12, ["Mountain Lion", "10.8"]], [11, ["Lion", "10.7"]], [10, ["Snow Leopard", "10.6"]], [9, ["Leopard", "10.5"]], [8, ["Tiger", "10.4"]], [7, ["Panther", "10.3"]], [6, ["Jaguar", "10.2"]], [5, ["Puma", "10.1"]] ]); function macosRelease(release) { release = Number((release || os2.release()).split(".")[0]); const [name, version] = nameMap.get(release) || ["Unknown", ""]; return { name, version }; } // src/index.ts import { backOff } from "exponential-backoff"; var clientVersion = "0.8.4"; var { SAUCE_VISUAL_BUILD_NAME, SAUCE_VISUAL_BRANCH_NAME, SAUCE_VISUAL_DEFAULT_BRANCH_NAME, SAUCE_VISUAL_PROJECT_NAME, SAUCE_VISUAL_PROJECT, SAUCE_VISUAL_BRANCH, SAUCE_VISUAL_DEFAULT_BRANCH, SAUCE_VISUAL_BUILD_ID, SAUCE_VISUAL_CUSTOM_ID } = process.env; function errorMsg(error) { return error.message || String(error); } function correctOsInfo({ osName, osVersion }) { var _a, _b; if (osName === "darwin") { return { name: OperatingSystem.Macos, version: (_b = (_a = macosRelease(osVersion)) == null ? void 0 : _a.version) != null ? _b : null }; } if (osName === "win32") { return { name: OperatingSystem.Windows, version: osVersion.split(".")[0] }; } return { name: null, version: null }; } function cypressBrowserToGraphQL(browser) { switch (browser) { case "chrome": return "CHROME"; case "firefox": return "FIREFOX"; } } var pluginInstance; var logger = console; var DiffNotReadyError = class extends Error { constructor(message) { super(message); this.name = "DiffNotReadyError"; } }; var CypressSauceVisual = class _CypressSauceVisual { constructor(config) { // A build can be managed externally (e.g by CLI) or by the Sauce Visual Cypress plugin this.isBuildExternal = false; this.screenshotsMetadata = {}; this.uploadedDiffIds = []; var _a, _b, _c, _d, _e, _f, _g; this.region = VisualApiRegion.fromName( process.env.SAUCE_REGION || ((_a = config.saucelabs) == null ? void 0 : _a.region) || "us-west-1" ); this.jobId = process.env.SAUCE_JOB_ID; this.api = getApi( { region: this.region.name, user: (_c = (_b = config.saucelabs) == null ? void 0 : _b.user) != null ? _c : process.env.SAUCE_USERNAME, key: (_e = (_d = config.saucelabs) == null ? void 0 : _d.key) != null ? _e : process.env.SAUCE_ACCESS_KEY }, { userAgent: `visual-cypress/${clientVersion}` } ); this.diffingMethod = (_f = config.saucelabs) == null ? void 0 : _f.diffingMethod; this.diffingOptions = (_g = config.saucelabs) == null ? void 0 : _g.diffingOptions; this.domCaptureScript = this.api.domCaptureScript(); } setBrowser(browser) { this.browser = browser; } setOperationSystem(os3) { this.os = os3; } useExistingBuildOrCreate(config) { return __async(this, null, function* () { this.buildId = yield this.getEffectiveBuild(); if (this.buildId) { this.isBuildExternal = true; } else { yield this.createBuild(config); } }); } getEffectiveBuild() { return __async(this, null, function* () { const buildCompletedMsg = `Sauce Labs Visual: cannot add more screenshots since the build is already completed`; if (SAUCE_VISUAL_BUILD_ID) { const build = yield this.api.build(SAUCE_VISUAL_BUILD_ID); if ((build == null ? void 0 : build.mode) == BuildMode.Completed) { logger.error(buildCompletedMsg); throw new Error(buildCompletedMsg); } return SAUCE_VISUAL_BUILD_ID; } if (SAUCE_VISUAL_CUSTOM_ID) { let build; try { build = yield this.api.buildByCustomId(SAUCE_VISUAL_CUSTOM_ID); } catch (e) { const msg = `Sauce Labs Visual: unable to fetch build for customId ${SAUCE_VISUAL_CUSTOM_ID}: ${errorMsg( e )}`; logger.error(msg); throw new Error(msg); } if ((build == null ? void 0 : build.mode) == BuildMode.Completed) { logger.error(buildCompletedMsg); throw new Error(buildCompletedMsg); } return build == null ? void 0 : build.id; } }); } /** * Build related actions */ createBuild(config) { return __async(this, null, function* () { var _a, _b, _c, _d; if (SAUCE_VISUAL_BRANCH_NAME) { logger.warn( "Sauce Labs Visual: SAUCE_VISUAL_BRANCH_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_BRANCH instead" ); } if (SAUCE_VISUAL_DEFAULT_BRANCH_NAME) { logger.warn( "Sauce Labs Visual: SAUCE_VISUAL_DEFAULT_BRANCH_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_DEFAULT_BRANCH instead" ); } if (SAUCE_VISUAL_PROJECT_NAME) { logger.warn( "Sauce Labs Visual: SAUCE_VISUAL_PROJECT_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_PROJECT instead" ); } try { const { id, url } = yield this.api.createBuild({ name: ((_a = config.saucelabs) == null ? void 0 : _a.buildName) || SAUCE_VISUAL_BUILD_NAME || "Cypress Visual Testing", project: ((_b = config.saucelabs) == null ? void 0 : _b.project) || SAUCE_VISUAL_PROJECT || SAUCE_VISUAL_PROJECT_NAME || null, branch: ((_c = config.saucelabs) == null ? void 0 : _c.branch) || SAUCE_VISUAL_BRANCH || SAUCE_VISUAL_BRANCH_NAME || null, defaultBranch: ((_d = config.saucelabs) == null ? void 0 : _d.defaultBranch) || SAUCE_VISUAL_DEFAULT_BRANCH || SAUCE_VISUAL_DEFAULT_BRANCH_NAME || null, customId: SAUCE_VISUAL_CUSTOM_ID || null }); this.buildId = id; logger.info(buildUrl(url)); } catch (e) { logger.error(` Sauce Labs Visual: Unable to create new build. ${source_default.red(errorMsg(ensureError(e).message))} `); logger.error(`The execution of Cypress has been interrupted.`); throw new Error("Sauce Labs Visual: Unable to create a build"); } }); } closeBuild() { return __async(this, null, function* () { if (!this.buildId) { throw new Error(`Sauce Labs Visual: Build was not created`); } if (this.isBuildExternal) { return; } try { const { url } = yield this.api.finishBuild({ uuid: this.buildId }); logger.info(buildUrl(url, { reviewReady: true })); } catch (e) { const msg = `Sauce Labs Visual: unable to finish build: ${errorMsg(e)}`; logger.error(msg); throw new Error(msg); } }); } /** * Screenshot management */ addScreenshotMetadata(metadata) { this.screenshotsMetadata[metadata.id] = metadata; return null; } getResultSummary() { return __async(this, null, function* () { return yield getVisualResults(this.api, { buildId: this.buildId, diffIds: this.uploadedDiffIds }); }); } getTestResults() { return __async(this, null, function* () { return yield backOff( () => __async(this, null, function* () { return yield this.getResultSummary(); }), { retry: (error) => { return !!error && error instanceof DiffNotReadyError; }, // See https://www.npmjs.com/package/exponential-backoff#backoffoptions for details delayFirstAttempt: true, timeMultiple: 2, numOfAttempts: 15, jitter: "full", startingDelay: 150, maxDelay: 1e4 } ); }); } processScreenshot(screenshot) { return __async(this, null, function* () { var _a, _b, _c, _d, _e, _f; const metadata = this.screenshotsMetadata[screenshot.name]; if (!metadata) return; const osInfo = correctOsInfo(this.os || { osName: "", osVersion: "" }); let hasFailedUpload = false; try { const screenshotId = yield this.api.uploadSnapshot({ buildId: (_a = this.buildId) != null ? _a : "", image: { path: screenshot.path }, dom: metadata.dom ? { data: Buffer.from(metadata.dom) } : void 0 }); const result = yield this.api.createSnapshot({ buildUuid: (_b = this.buildId) != null ? _b : "", uploadUuid: screenshotId, name: metadata.name, browser: (_d = cypressBrowserToGraphQL((_c = this.browser) == null ? void 0 : _c.name)) != null ? _d : null, browserVersion: (_f = (_e = this.browser) == null ? void 0 : _e.version) != null ? _f : null, operatingSystem: osInfo.name, operatingSystemVersion: osInfo.version, suiteName: metadata.suiteName, testName: metadata.testName, ignoreRegions: metadata.regions.map((r) => __spreadProps(__spreadValues({}, r.element), { diffingOptions: selectiveRegionOptionsToDiffingOptions(r) })), device: metadata.viewport ? `Desktop (${metadata.viewport.width}x${metadata.viewport.height})` : "Desktop", devicePixelRatio: metadata.devicePixelRatio, diffingMethod: metadata.diffingMethod || this.diffingMethod || DiffingMethod.Balanced, jobUrl: this.jobId ? this.region.jobUrl(this.jobId) : void 0 }); logger.info(` ${source_default.green("\u2714")} ${metadata.name} `); this.uploadedDiffIds.push( ...result.diffs.nodes.flatMap((diff) => diff.id) ); } catch (e) { logger.error( ` ${source_default.red("\u2716")} ${metadata.name}: upload failed (${errorMsg( e )}))` ); logger.error(e); hasFailedUpload = true; } if (hasFailedUpload) { logger.error( `Sauce Labs Visual: Some screenshots have not been uploaded successfully.` ); logger.error(`The execution of Cypress has been interrupted.`); throw new Error("Sauce Labs Visual: Failed to upload some screenshots"); } logger.info(); }); } /** * Cleans up leftover screenshot metadata after a spec is complete. */ cleanScreenshot() { this.screenshotsMetadata = {}; } /** * Wrapper around logging */ log(data) { logger.info(data); return null; } logColoredWarn(data) { logger.warn(source_default.yellow(data)); return null; } /** * Registration of hooks */ static register(on, config) { const pluginConfig = config; const plugin = pluginInstance != null ? pluginInstance : new _CypressSauceVisual(pluginConfig); pluginInstance = plugin; on("before:run", (details) => __async(this, null, function* () { if (details.browser) { plugin.setBrowser(details.browser); } plugin.setOperationSystem(details.system); yield plugin.useExistingBuildOrCreate(pluginConfig); })); on("before:spec", () => { plugin.uploadedDiffIds = []; }); on( "after:screenshot", (details) => __async(this, null, function* () { yield plugin.processScreenshot(details); return details; }) ); on("after:run", () => __async(this, null, function* () { yield plugin.closeBuild(); })); on("after:spec", () => __async(this, null, function* () { plugin.cleanScreenshot(); })); on("task", { "get-script": function() { return __async(this, null, function* () { return pluginInstance == null ? void 0 : pluginInstance.domCaptureScript; }); }, "visual-register-screenshot": function(metadata) { return plugin.addScreenshotMetadata(metadata); }, "visual-log-capture": function({ screenshotName }) { const greyMessage = `sauce-visual: ${screenshotName} `; logger.info(` ${source_default.green("\u2714")} ${source_default.grey(greyMessage)}`); return null; }, "visual-log": function(payload) { switch (payload.level) { case "info": logger.info(payload.msg); case "warn": logger.warn(payload.msg); case "error": logger.error(payload.msg); } return null; }, "visual-test-results": function() { return __async(this, null, function* () { return yield plugin.getTestResults(); }); } }); } }; export { CypressSauceVisual, DiffingMethod };