@saucelabs/visual
Version:
JS client bindings for Sauce Labs Visual
325 lines (323 loc) • 9.13 kB
JavaScript
import {
BaselinesOrderBy,
BranchesOrderBy,
Browser,
BuildByCustomIdDocument,
BuildDocument,
BuildMode,
BuildStatus,
BuildStatusDocument,
BuildWithDiffsByCustomIdDocument,
BuildWithDiffsDocument,
BuildsOrderBy,
CommentAction,
CommentsOrderBy,
CreateBuildDocument,
CreateSnapshotDocument,
CreateSnapshotFromWebDriverDocument,
CreateSnapshotUploadDocument,
DiffFeedbacksOrderBy,
DiffStatus,
DiffingMethod,
DiffsForTestResultDocument,
DiffsOrderBy,
DomFormat,
FinishBuildDocumentDocument,
GroupByOption,
MergeBaselinesDocument,
OperatingSystem,
OrgStatsOrderBy,
ProjectsOrderBy,
ScrollOption,
SelectorType,
SnapshotsOrderBy,
UpdateDiffDocument,
UpdateDiffStatus,
VisualApiRegion,
WebdriverSessionInfoDocument,
displayStatusTable,
ensureError,
getApi,
regions,
selectiveRegionOptionsToDiffingOptions,
selectiveRegionToRegionIn,
selectiveRegionsToRegionIn,
selectiveRegionsToRegionInSync
} from "./chunk-JCFCNGGA.js";
import "./chunk-3IT7HMR5.js";
// src/utils.ts
import { type } from "arktype";
import fs from "fs/promises";
import * as os from "os";
import { backOff } from "exponential-backoff";
var getFullPageConfig = async (main, local, getId) => {
const isNoConfig = !main && !local;
const isLocalOff = local === false;
if (isNoConfig || isLocalOff) {
return;
}
const globalCfg = typeof main === "object" ? main : {};
const localCfg = typeof local === "object" ? local : {};
const { scrollElement, ...rest } = { ...globalCfg, ...localCfg };
const result = rest;
if (scrollElement && getId) {
result.scrollElement = await getId(await scrollElement);
}
return result;
};
var isSkipMode = () => {
const VISUAL_SKIP_ENV_VAR = "SAUCE_VISUAL_SKIP";
return Boolean(process.env[VISUAL_SKIP_ENV_VAR]);
};
var makeValidate = (t) => (x) => {
const { data, problems } = t(x);
if (problems) {
throw new Error(problems.toLocaleString());
}
return data;
};
var ignoreRegionType = type({
width: "integer",
height: "integer",
x: "integer",
y: "integer",
"name?": "string | null | undefined"
});
var isIgnoreRegion = ignoreRegionType.allows;
var validateIgnoreRegion = makeValidate(ignoreRegionType);
var elementIn = type({
id: "string",
"name?": "string | undefined"
});
var isElementIn = elementIn.allows;
var validateElementIn = makeValidate(elementIn);
var regionType = type([
{
element: "unknown",
enableOnly: "any"
},
"|",
{
element: "unknown",
disableOnly: "any"
}
]);
var isRegionType = (item) => typeof item === "object" && regionType.allows(item);
var isIgnoreSelectorType = (item) => typeof item === "object" && typeof item.selector === "object" && Object.values(SelectorType).includes(item.selector.type) && typeof item.selector.value === "string";
var validateRegionType = makeValidate(elementIn);
var getDiffingOptions = (region) => {
const { element: __, ...rest } = region;
if ("enableOnly" in rest || "disableOnly" in rest) {
return selectiveRegionOptionsToDiffingOptions(rest);
}
return void 0;
};
var parseRegionsForAPI = async (ignore, resolveItem) => {
const promisedIgnorables = ignore.map(
async (itemOrRegionOrSelector) => {
const { item, diffingOptions } = (() => {
if (isRegionType(itemOrRegionOrSelector)) {
return {
item: itemOrRegionOrSelector.element,
diffingOptions: getDiffingOptions(itemOrRegionOrSelector)
};
} else if (isIgnoreSelectorType(itemOrRegionOrSelector)) {
return {
item: itemOrRegionOrSelector,
diffingOptions: itemOrRegionOrSelector.diffingOptions
};
}
return { item: itemOrRegionOrSelector, diffingOptions: void 0 };
})();
const elements = isIgnoreRegion(item) || isIgnoreSelectorType(item) ? [item] : await resolveItem(item);
return elements.map((element) => ({
...element,
diffingOptions
}));
}
);
const flattened = (await Promise.all(promisedIgnorables)).flat();
return {
ignoreRegions: flattened.filter(isIgnoreRegion),
ignoreElements: flattened.filter(isElementIn),
ignoreSelectors: flattened.filter(isIgnoreSelectorType)
};
};
var domScriptPath = `${os.tmpdir()}/sauce-visual-dom-capture.js`;
var downloadDomScript = async (api) => {
try {
const script = await api.domCaptureScript();
script && await fs.writeFile(domScriptPath, script);
} catch (err) {
console.error(`Cannot get dom capturing script.
${err}`);
}
};
var getDomScript = async () => {
try {
return (await fs.readFile(domScriptPath)).toString();
} catch (err) {
if (err.code !== "ENOENT") {
console.error(err);
}
}
};
var removeDomScriptFile = async () => {
try {
await fs.unlink(domScriptPath);
} catch (err) {
}
};
var getEnvOpts = () => {
const {
SAUCE_USERNAME,
SAUCE_ACCESS_KEY,
SAUCE_REGION,
SAUCE_BUILD_NAME,
SAUCE_BRANCH_NAME,
SAUCE_DEFAULT_BRANCH_NAME,
SAUCE_PROJECT_NAME,
SAUCE_VISUAL_BUILD_NAME,
SAUCE_VISUAL_PROJECT,
SAUCE_VISUAL_BRANCH,
SAUCE_VISUAL_DEFAULT_BRANCH,
SAUCE_VISUAL_BUILD_ID,
SAUCE_VISUAL_CUSTOM_ID
} = process.env;
if (SAUCE_BUILD_NAME) {
console.warn(
"Sauce Labs Visual: SAUCE_BUILD_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_BUILD_NAME instead"
);
}
if (SAUCE_BRANCH_NAME) {
console.warn(
"Sauce Labs Visual: SAUCE_BRANCH_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_BRANCH instead"
);
}
if (SAUCE_DEFAULT_BRANCH_NAME) {
console.warn(
"Sauce Labs Visual: SAUCE_DEFAULT_BRANCH_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_DEFAULT_BRANCH instead"
);
}
if (SAUCE_PROJECT_NAME) {
console.warn(
"Sauce Labs Visual: SAUCE_PROJECT_NAME is deprecated and will be removed in a future version. Please use SAUCE_VISUAL_PROJECT instead"
);
}
return {
branch: SAUCE_VISUAL_BRANCH || SAUCE_BRANCH_NAME || null,
defaultBranch: SAUCE_VISUAL_DEFAULT_BRANCH || SAUCE_DEFAULT_BRANCH_NAME || null,
buildId: SAUCE_VISUAL_BUILD_ID ?? null,
project: SAUCE_VISUAL_PROJECT || SAUCE_PROJECT_NAME || null,
user: SAUCE_USERNAME ?? void 0,
key: SAUCE_ACCESS_KEY ?? void 0,
region: SAUCE_REGION ?? "us-west-1",
buildName: SAUCE_VISUAL_BUILD_NAME || SAUCE_BUILD_NAME || "Sauce Visual Build",
customId: SAUCE_VISUAL_CUSTOM_ID ?? null
};
};
var getVisualResults = async (api, filter) => {
const { buildId, diffIds } = filter;
const initialStatusSummary = {
["APPROVED" /* Approved */]: 0,
["EQUAL" /* Equal */]: 0,
["UNAPPROVED" /* Unapproved */]: 0,
["REJECTED" /* Rejected */]: 0,
["QUEUED" /* Queued */]: 0,
["ERRORED" /* Errored */]: 0
};
if (!buildId) {
throw new Error(
"No Sauce Visual build present, cannot determine visual results."
);
}
if (diffIds.length === 0) {
return initialStatusSummary;
}
return await backOff(
async () => {
const summary = { ...initialStatusSummary };
const diffsForTestResult = await api.diffsForTestResult(buildId);
if (!diffsForTestResult) {
return summary;
}
const filterDiffsById = (diff) => diffIds.includes(diff.id);
const statusSummary = diffsForTestResult.nodes.filter(filterDiffsById).reduce((statusSummary2, diff) => {
statusSummary2[diff.status]++;
return statusSummary2;
}, summary);
if (statusSummary["QUEUED" /* Queued */] > 0) {
throw new Error("Some diffs are still in the queued state.");
}
return statusSummary;
},
{
maxDelay: 1e4,
numOfAttempts: 10
}
);
};
export {
BaselinesOrderBy,
BranchesOrderBy,
Browser,
BuildByCustomIdDocument,
BuildDocument,
BuildMode,
BuildStatus,
BuildStatusDocument,
BuildWithDiffsByCustomIdDocument,
BuildWithDiffsDocument,
BuildsOrderBy,
CommentAction,
CommentsOrderBy,
CreateBuildDocument,
CreateSnapshotDocument,
CreateSnapshotFromWebDriverDocument,
CreateSnapshotUploadDocument,
DiffFeedbacksOrderBy,
DiffStatus,
DiffingMethod,
DiffsForTestResultDocument,
DiffsOrderBy,
DomFormat,
FinishBuildDocumentDocument,
GroupByOption,
MergeBaselinesDocument,
OperatingSystem,
OrgStatsOrderBy,
ProjectsOrderBy,
ScrollOption,
SelectorType,
SnapshotsOrderBy,
UpdateDiffDocument,
UpdateDiffStatus,
VisualApiRegion,
WebdriverSessionInfoDocument,
displayStatusTable,
domScriptPath,
downloadDomScript,
ensureError,
getApi,
getDiffingOptions,
getDomScript,
getEnvOpts,
getFullPageConfig,
getVisualResults,
isElementIn,
isIgnoreRegion,
isIgnoreSelectorType,
isRegionType,
isSkipMode,
makeValidate,
parseRegionsForAPI,
regions,
removeDomScriptFile,
selectiveRegionOptionsToDiffingOptions,
selectiveRegionToRegionIn,
selectiveRegionsToRegionIn,
selectiveRegionsToRegionInSync,
validateElementIn,
validateIgnoreRegion,
validateRegionType
};