@saucelabs/cypress-visual-plugin
Version:
Cypress plugin for Sauce Visual Testing
915 lines (904 loc) • 28.4 kB
JavaScript
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
};