@argos-ci/browser
Version:
Browser utilities to stabilize visual testing with Argos.
440 lines (420 loc) • 12.7 kB
JavaScript
;
(() => {
// src/global/media.ts
function getColorScheme() {
const { colorScheme } = window.getComputedStyle(document.body);
return colorScheme === "dark" || colorScheme === "dark only" || window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function getMediaType() {
return window.matchMedia("print").matches ? "print" : "screen";
}
// src/global/stabilization/plugins/addArgosClass.ts
var plugin = {
name: "addArgosClass",
beforeAll() {
const className = "__argos__";
document.documentElement.classList.add(className);
return () => {
document.documentElement.classList.remove(className);
};
}
};
// src/global/stabilization/util.ts
function injectGlobalStyles(css, id) {
const style = document.createElement("style");
style.textContent = css.trim();
style.id = `argos-${id}`;
document.head.appendChild(style);
return () => {
const style2 = document.getElementById(`argos-${id}`);
if (style2) {
style2.remove();
}
};
}
// src/global/stabilization/plugins/addArgosCSS.ts
var plugin2 = {
name: "addArgosCSS",
beforeAll(options) {
if (options.argosCSS) {
return injectGlobalStyles(options.argosCSS, "custom-css");
}
return void 0;
}
};
// src/global/stabilization/plugins/argosHelpers.ts
var plugin3 = {
name: "argosHelpers",
beforeAll() {
const styles = `
/* Make the element transparent */
[data-visual-test="transparent"] {
color: transparent !important;
font-family: monospace !important;
opacity: 0 !important;
}
/* Remove the element */
[data-visual-test="removed"] {
display: none !important;
}
/* Disable radius */
[data-visual-test-no-radius]:not([data-visual-test-no-radius="false"]) {
border-radius: 0 !important;
}
`;
return injectGlobalStyles(styles, "argos-helpers");
}
};
// src/global/stabilization/plugins/disableSpellCheck.ts
var UNSET = "--unset";
var BACKUP_ATTRIBUTE = "data-argos-bck-spellcheck";
var plugin4 = {
name: "disableSpellcheck",
beforeAll() {
document.querySelectorAll(
"[contenteditable]:not([contenteditable=false]), input, textarea"
).forEach((element) => {
const spellcheck = element.getAttribute("spellcheck");
if (spellcheck === "false") {
return;
}
element.setAttribute(BACKUP_ATTRIBUTE, spellcheck ?? UNSET);
element.setAttribute("spellcheck", "false");
});
return () => {
document.querySelectorAll(`[${BACKUP_ATTRIBUTE}]`).forEach((input) => {
const bckSpellcheck = input.getAttribute(BACKUP_ATTRIBUTE);
if (bckSpellcheck === UNSET) {
input.removeAttribute("spellcheck");
} else if (bckSpellcheck) {
input.setAttribute("spellcheck", bckSpellcheck);
}
input.removeAttribute(BACKUP_ATTRIBUTE);
});
};
}
};
// src/global/stabilization/plugins/fontAntialiasing.ts
var plugin5 = {
name: "fontAntialiasing",
beforeAll() {
return injectGlobalStyles(
`* { -webkit-font-smoothing: antialiased !important; }`,
"font-antialiasing"
);
}
};
// src/global/stabilization/plugins/hideCarets.ts
var plugin6 = {
name: "hideCarets",
beforeAll() {
return injectGlobalStyles(
`* { caret-color: transparent !important; }`,
"hide-carets"
);
}
};
// src/global/stabilization/plugins/hideScrollbars.ts
var plugin7 = {
name: "hideScrollbars",
beforeAll() {
return injectGlobalStyles(
`::-webkit-scrollbar { display: none !important; }`,
"hide-scrollbars"
);
}
};
// src/global/stabilization/plugins/loadImageSrcset.ts
var plugin8 = {
name: "loadImageSrcset",
beforeEach(options) {
if (!options.viewports || options.viewports.length === 0) {
return void 0;
}
function getLargestSrcFromSrcset(srcset) {
const sources = srcset.split(",").map((item) => {
const [url, size] = item.trim().split(/\s+/);
if (!url) {
return null;
}
const widthMatch = size && size.match(/^(\d+)w$/);
if (!widthMatch) {
return { url, width: 0 };
}
const width = parseInt(widthMatch[1], 10);
return { url, width };
}).filter((x) => x !== null);
if (sources.length === 0) {
return srcset;
}
const largest = sources.reduce(
(max, curr) => curr.width > max.width ? curr : max
);
return largest.url;
}
function forceSrcsetReload(img) {
const srcset = img.getAttribute("srcset");
if (!srcset) {
return;
}
img.setAttribute("srcset", getLargestSrcFromSrcset(srcset));
}
Array.from(document.querySelectorAll("img,source")).forEach(
forceSrcsetReload
);
}
};
// src/global/stabilization/plugins/roundImageSize.ts
var BACKUP_ATTRIBUTE_WIDTH = "data-argos-bck-width";
var BACKUP_ATTRIBUTE_HEIGHT = "data-argos-bck-height";
var plugin9 = {
name: "roundImageSize",
beforeEach() {
Array.from(document.images).forEach((img) => {
if (!img.complete) {
return;
}
img.setAttribute(BACKUP_ATTRIBUTE_WIDTH, img.style.width);
img.setAttribute(BACKUP_ATTRIBUTE_HEIGHT, img.style.height);
img.style.width = `${Math.round(img.offsetWidth)}px`;
img.style.height = `${Math.round(img.offsetHeight)}px`;
});
return () => {
Array.from(document.images).forEach((img) => {
const bckWidth = img.getAttribute(BACKUP_ATTRIBUTE_WIDTH);
const bckHeight = img.getAttribute(BACKUP_ATTRIBUTE_HEIGHT);
if (bckWidth === null && bckHeight === null) {
return;
}
img.style.width = bckWidth ?? "";
img.style.height = bckHeight ?? "";
img.removeAttribute(BACKUP_ATTRIBUTE_WIDTH);
img.removeAttribute(BACKUP_ATTRIBUTE_HEIGHT);
});
};
}
};
// src/global/stabilization/plugins/stabilizeSticky.ts
var BACKUP_ATTRIBUTE2 = "data-argos-bck-position";
function setAndBackupPosition(element, position) {
const previousPosition = element.style.position;
const previousRect = element.getBoundingClientRect();
element.style.position = position;
const currentRect = element.getBoundingClientRect();
if (previousRect.x !== currentRect.x || previousRect.y !== currentRect.y) {
element.style.position = previousPosition;
return;
}
element.setAttribute(BACKUP_ATTRIBUTE2, previousPosition ?? "unset");
}
var plugin10 = {
name: "stabilizeSticky",
beforeAll(options) {
if (!options.fullPage) {
return void 0;
}
document.querySelectorAll("*").forEach((element) => {
if (!(element instanceof HTMLElement)) {
return;
}
if (element.tagName === "IFRAME") {
return;
}
const style = window.getComputedStyle(element);
const { position } = style;
if (position === "fixed") {
setAndBackupPosition(element, "absolute");
} else if (position === "sticky") {
setAndBackupPosition(element, "relative");
}
});
return () => {
document.querySelectorAll(`[${BACKUP_ATTRIBUTE2}]`).forEach((element) => {
if (!(element instanceof HTMLElement)) {
return;
}
const position = element.getAttribute(BACKUP_ATTRIBUTE2);
if (!position) {
return;
}
if (position === "unset") {
element.style.removeProperty("position");
} else {
element.style.position = position;
}
element.removeAttribute(BACKUP_ATTRIBUTE2);
});
};
}
};
// src/global/stabilization/plugins/waitForAriaBusy.ts
function checkIsElementVisible(element) {
if (element instanceof HTMLElement && (element.offsetHeight !== 0 || element.offsetWidth !== 0)) {
return true;
}
return element.getClientRects().length > 0;
}
var plugin11 = {
name: "waitForAriaBusy",
wait: {
for: () => {
return Array.from(document.querySelectorAll('[aria-busy="true"]')).every(
(element) => !checkIsElementVisible(element)
);
},
failureExplanation: "Some elements still have `aria-busy='true'`"
}
};
// src/global/stabilization/plugins/waitForFonts.ts
var plugin12 = {
name: "waitForFonts",
wait: {
for: () => {
return document.fonts.status === "loaded";
},
failureExplanation: "Some fonts have not been loaded"
}
};
// src/global/stabilization/plugins/waitForImages.ts
var plugin13 = {
name: "waitForImages",
beforeEach() {
Array.from(document.images).every((img) => {
if (img.decoding !== "sync") {
img.decoding = "sync";
}
if (img.loading !== "eager") {
img.loading = "eager";
}
});
return void 0;
},
wait: {
for: () => {
const images = Array.from(document.images);
const results = images.map((img) => {
if (img.decoding !== "sync") {
img.decoding = "sync";
}
if (img.loading !== "eager") {
img.loading = "eager";
}
return img.complete;
});
return results.every((x) => x);
},
failureExplanation: "Some images have not been loaded"
}
};
// src/global/stabilization/plugins/index.ts
var corePlugins = [plugin, plugin2, plugin3];
var plugins = [
plugin4,
plugin5,
plugin6,
plugin7,
plugin8,
plugin9,
plugin10,
plugin11,
plugin12,
plugin13
];
// src/global/stabilization/index.ts
var beforeAllCleanups = /* @__PURE__ */ new Set();
var beforeEachCleanups = /* @__PURE__ */ new Set();
function getPlugins(context) {
const enabledPlugins = plugins.filter((plugin14) => {
if (context.options === false) {
return false;
}
if (typeof context.options === "object") {
const pluginEnabled = context.options[plugin14.name];
if (pluginEnabled === false) {
return false;
}
}
return true;
});
return [...corePlugins, ...enabledPlugins];
}
function getPluginByName(name) {
const plugin14 = plugins.find((p) => p.name === name);
if (!plugin14) {
throw new Error(`Invariant: plugin ${name} not found`);
}
return plugin14;
}
function beforeAll(context = {}) {
getPlugins(context).forEach((plugin14) => {
if (plugin14.beforeAll) {
const cleanup = plugin14.beforeAll(context);
if (cleanup) {
beforeAllCleanups.add(cleanup);
}
}
});
}
function afterAll() {
beforeAllCleanups.forEach((cleanup) => {
cleanup();
});
beforeAllCleanups.clear();
}
function beforeEach(context = {}) {
getPlugins(context).forEach((plugin14) => {
if (plugin14.beforeEach) {
const cleanup = plugin14.beforeEach(context);
if (cleanup) {
beforeEachCleanups.add(cleanup);
}
}
});
}
function afterEach() {
beforeEachCleanups.forEach((cleanup) => {
cleanup();
});
beforeEachCleanups.clear();
}
function getStabilityState(context) {
const stabilityState = {};
getPlugins(context).forEach((plugin14) => {
if (plugin14.wait) {
stabilityState[plugin14.name] = plugin14.wait.for(context);
}
});
return stabilityState;
}
function waitFor(context) {
const stabilityState = getStabilityState(context);
return Object.values(stabilityState).every(Boolean);
}
function getWaitFailureExplanations(options) {
const stabilityState = getStabilityState(options);
const failedPlugins = Object.entries(stabilityState).filter(
([, value]) => !value
);
return failedPlugins.map(([name]) => {
const plugin14 = getPluginByName(name);
if (!plugin14.wait) {
throw new Error(
`Invariant: plugin ${name} does not have a wait function`
);
}
return plugin14.wait.failureExplanation;
});
}
// src/global/index.ts
var ArgosGlobal = {
beforeAll,
afterAll,
beforeEach,
afterEach,
waitFor,
getWaitFailureExplanations,
getColorScheme: () => getColorScheme(),
getMediaType: () => getMediaType()
};
window.__ARGOS__ = ArgosGlobal;
})();