@argos-ci/browser
Advanced tools
Comparing version
@@ -122,2 +122,3 @@ type ViewportOrientation = "portrait" | "landscape"; | ||
getStabilityFailureReasons: (options?: StabilizationOptions) => string[]; | ||
afterEach: () => void; | ||
setup: (options?: SetupOptions) => void; | ||
@@ -124,0 +125,0 @@ teardown: (options?: TeardownOptions) => void; |
@@ -156,2 +156,4 @@ "use strict"; | ||
img.dataset.argosStabilization = "complete"; | ||
img.removeEventListener("load", update); | ||
img.removeEventListener("error", update); | ||
}; | ||
@@ -162,3 +164,4 @@ if (img.complete) { | ||
} | ||
img.addEventListener("load", update, { once: true }); | ||
img.addEventListener("load", update); | ||
img.addEventListener("error", update); | ||
return false; | ||
@@ -168,14 +171,2 @@ }); | ||
} | ||
function restoreImageSizes(document2) { | ||
const images = Array.from(document2.images); | ||
images.forEach((img) => { | ||
if (img.dataset.argosStabilization) { | ||
img.style.width = img.dataset.argosBckWidth ?? ""; | ||
img.style.height = img.dataset.argosBckHeight ?? ""; | ||
delete img.dataset.argosBckWidth; | ||
delete img.dataset.argosBckHeight; | ||
delete img.dataset.argosStabilization; | ||
} | ||
}); | ||
} | ||
function addGlobalClass(document2, className) { | ||
@@ -208,3 +199,2 @@ document2.documentElement.classList.add(className); | ||
} | ||
restoreImageSizes(document2); | ||
} | ||
@@ -243,3 +233,3 @@ function waitForFontsToLoad(document2) { | ||
} = options ?? {}; | ||
const imagesLoaded = images || imageSizes ? waitForImagesToLoad(document2) : null; | ||
const imagesLoaded = images || imageSizes ? waitForImagesToLoad(document2) : false; | ||
return { | ||
@@ -287,2 +277,3 @@ ariaBusy: ariaBusy ? waitForNoBusy(document2) : true, | ||
getStabilityFailureReasons: (options) => getStabilityFailureReasons(document, options), | ||
afterEach: () => teardown(document), | ||
setup: (options = {}) => setup(document, options), | ||
@@ -289,0 +280,0 @@ teardown: (options = {}) => teardown(document, options), |
@@ -44,3 +44,3 @@ // src/viewport.ts | ||
function getGlobalScript() { | ||
return '"use strict";\n(() => {\n // src/global/stabilization.ts\n var GLOBAL_CSS = `\n/* Hide carets */\n* {\n caret-color: transparent !important;\n}\n\n/* Reduce text-aliasing issues in Blink browsers */\n* {\n -webkit-font-smoothing: antialiased !important;\n}\n\n/* Hide scrollbars */\n::-webkit-scrollbar {\n display: none !important;\n}\n\n/* Make the element transparent */\n[data-visual-test="transparent"] {\n color: transparent !important;\n font-family: monospace !important;\n opacity: 0 !important;\n}\n\n/* Remove the element */\n[data-visual-test="removed"] {\n display: none !important;\n}\n\n/* Disable radius */\n[data-visual-test-no-radius]:not([data-visual-test-no-radius="false"]) {\n border-radius: 0 !important;\n}\n`;\n function setAndBackupSpellcheck(element, spellcheck) {\n element.setAttribute(\n "data-argos-bck-spellcheck",\n element.getAttribute("spellcheck") ?? "unset"\n );\n element.setAttribute("spellcheck", spellcheck);\n }\n var SPELL_CHECK_QUERY = "[contenteditable]:not([contenteditable=false]):not([spellcheck=false]), input:not([spellcheck=false]), textarea:not([spellcheck=false])";\n function disableSpellCheck(document2) {\n const inputs = document2.querySelectorAll(SPELL_CHECK_QUERY);\n inputs.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n setAndBackupSpellcheck(element, "false");\n });\n }\n function restoreSpellCheck(document2) {\n const inputs = document2.querySelectorAll(SPELL_CHECK_QUERY);\n inputs.forEach((input) => {\n const spellcheck = input.getAttribute("data-argos-bck-spellcheck");\n if (spellcheck === "unset") {\n input.removeAttribute("spellcheck");\n } else if (spellcheck) {\n input.setAttribute("spellcheck", spellcheck);\n }\n input.removeAttribute("data-argos-bck-spellcheck");\n });\n }\n function injectGlobalStyles(document2, css, id) {\n const style = document2.createElement("style");\n style.textContent = css;\n style.id = id;\n document2.head.appendChild(style);\n }\n function removeGlobalStyles(document2, id) {\n const style = document2.getElementById(id);\n if (style) {\n style.remove();\n }\n }\n var checkIsHTMLElement = (element) => {\n return "style" in element;\n };\n function setAndBackupPosition(element, position) {\n const previousPosition = element.style.position;\n const previousRect = element.getBoundingClientRect();\n element.style.position = position;\n const currentRect = element.getBoundingClientRect();\n if (previousRect.x !== currentRect.x || previousRect.y !== currentRect.y) {\n element.style.position = previousPosition;\n return;\n }\n element.setAttribute("data-argos-bck-position", previousPosition ?? "unset");\n }\n function stabilizeElementPositions(document2) {\n const window2 = document2.defaultView;\n if (!window2) {\n return;\n }\n const elements = Array.from(document2.querySelectorAll("*"));\n elements.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n if (element.tagName === "IFRAME") {\n return;\n }\n const style = window2.getComputedStyle(element);\n const position = style.position;\n if (position === "fixed") {\n setAndBackupPosition(element, "absolute");\n } else if (position === "sticky") {\n setAndBackupPosition(element, "relative");\n }\n });\n }\n function restoreElementPositions(document2) {\n const window2 = document2.defaultView;\n if (!window2) {\n return;\n }\n const elements = Array.from(document2.querySelectorAll("*"));\n elements.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n const position = element.getAttribute("data-argos-bck-position");\n if (position === "unset") {\n element.style.removeProperty("position");\n } else if (position) {\n element.style.position = position;\n }\n element.removeAttribute("data-argos-bck-position");\n });\n }\n function stabilizeImageSizes(document2) {\n const images = Array.from(document2.images);\n const results = images.map((img) => {\n if (img.dataset.argosStabilization === "pending") {\n return false;\n }\n if (img.dataset.argosStabilization === "complete") {\n return true;\n }\n img.dataset.argosStabilization = "pending";\n const originalSrcSet = img.srcset;\n const originalSrc = img.src;\n img.srcset = "";\n img.src = "";\n img.srcset = originalSrcSet;\n img.src = originalSrc;\n const update = () => {\n img.dataset.argosBckWidth = img.style.width ?? "";\n img.dataset.argosBckHeight = img.style.height ?? "";\n const rect = img.getBoundingClientRect();\n img.style.width = `${Math.round(rect.width)}px`;\n img.style.height = `${Math.round(rect.height)}px`;\n img.dataset.argosStabilization = "complete";\n };\n if (img.complete) {\n update();\n return true;\n }\n img.addEventListener("load", update, { once: true });\n return false;\n });\n return results.every((x) => x);\n }\n function restoreImageSizes(document2) {\n const images = Array.from(document2.images);\n images.forEach((img) => {\n if (img.dataset.argosStabilization) {\n img.style.width = img.dataset.argosBckWidth ?? "";\n img.style.height = img.dataset.argosBckHeight ?? "";\n delete img.dataset.argosBckWidth;\n delete img.dataset.argosBckHeight;\n delete img.dataset.argosStabilization;\n }\n });\n }\n function addGlobalClass(document2, className) {\n document2.documentElement.classList.add(className);\n }\n function removeGlobalClass(document2, className) {\n document2.documentElement.classList.remove(className);\n }\n function setup(document2, { fullPage, argosCSS } = {}) {\n addGlobalClass(document2, "__argos__");\n injectGlobalStyles(document2, GLOBAL_CSS, "argos-reset-style");\n if (argosCSS) {\n injectGlobalStyles(document2, argosCSS, "argos-user-style");\n }\n disableSpellCheck(document2);\n if (fullPage) {\n stabilizeElementPositions(document2);\n }\n }\n function teardown(document2, { fullPage, argosCSS } = {}) {\n removeGlobalClass(document2, "__argos__");\n removeGlobalStyles(document2, "argos-reset-style");\n if (argosCSS) {\n removeGlobalStyles(document2, "argos-user-style");\n }\n restoreSpellCheck(document2);\n if (fullPage) {\n restoreElementPositions(document2);\n }\n restoreImageSizes(document2);\n }\n function waitForFontsToLoad(document2) {\n return document2.fonts.status === "loaded";\n }\n function waitForImagesToLoad(document2) {\n const images = Array.from(document2.images);\n return images.every((img) => {\n if (img.decoding !== "sync") {\n img.decoding = "sync";\n }\n if (img.loading !== "eager") {\n img.loading = "eager";\n }\n return img.complete;\n });\n }\n function waitForNoBusy(document2) {\n const checkIsVisible = (element) => {\n if (element instanceof HTMLElement && (element.offsetHeight !== 0 || element.offsetWidth !== 0)) {\n return true;\n }\n return element.getClientRects().length > 0;\n };\n const elements = Array.from(document2.querySelectorAll(\'[aria-busy="true"]\'));\n return elements.every((element) => !checkIsVisible(element));\n }\n function getStabilityState(document2, options) {\n const {\n ariaBusy = true,\n images = true,\n fonts = true,\n imageSizes = true\n } = options ?? {};\n const imagesLoaded = images || imageSizes ? waitForImagesToLoad(document2) : null;\n return {\n ariaBusy: ariaBusy ? waitForNoBusy(document2) : true,\n images: images ? imagesLoaded : true,\n imageSizes: imageSizes ? imagesLoaded ? stabilizeImageSizes(document2) : false : true,\n fonts: fonts ? waitForFontsToLoad(document2) : true\n };\n }\n var VALIDATION_ERRORS = {\n ariaBusy: "Some elements still have `aria-busy=\'true\'`",\n images: "Some images are still loading",\n imageSizes: "Failed to stabilize image sizes",\n fonts: "Some fonts are still loading"\n };\n function getStabilityFailureReasons(document2, options) {\n const stabilityState = getStabilityState(document2, options);\n return Object.entries(stabilityState).reduce(\n (reasons, [key, value]) => {\n if (!value) {\n reasons.push(VALIDATION_ERRORS[key]);\n }\n return reasons;\n },\n []\n );\n }\n function checkIsStable(document2, options) {\n const stabilityState = getStabilityState(document2, options);\n return Object.values(stabilityState).every(Boolean);\n }\n\n // src/global/media.ts\n function getColorScheme(window2) {\n return window2.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";\n }\n function getMediaType(window2) {\n return window2.matchMedia("print").matches ? "print" : "screen";\n }\n\n // src/global/index.ts\n var ArgosGlobal = {\n checkIsStable: (options) => checkIsStable(document, options),\n getStabilityFailureReasons: (options) => getStabilityFailureReasons(document, options),\n setup: (options = {}) => setup(document, options),\n teardown: (options = {}) => teardown(document, options),\n getColorScheme: () => getColorScheme(window),\n getMediaType: () => getMediaType(window)\n };\n window.__ARGOS__ = ArgosGlobal;\n})();\n'; | ||
return '"use strict";\n(() => {\n // src/global/stabilization.ts\n var GLOBAL_CSS = `\n/* Hide carets */\n* {\n caret-color: transparent !important;\n}\n\n/* Reduce text-aliasing issues in Blink browsers */\n* {\n -webkit-font-smoothing: antialiased !important;\n}\n\n/* Hide scrollbars */\n::-webkit-scrollbar {\n display: none !important;\n}\n\n/* Make the element transparent */\n[data-visual-test="transparent"] {\n color: transparent !important;\n font-family: monospace !important;\n opacity: 0 !important;\n}\n\n/* Remove the element */\n[data-visual-test="removed"] {\n display: none !important;\n}\n\n/* Disable radius */\n[data-visual-test-no-radius]:not([data-visual-test-no-radius="false"]) {\n border-radius: 0 !important;\n}\n`;\n function setAndBackupSpellcheck(element, spellcheck) {\n element.setAttribute(\n "data-argos-bck-spellcheck",\n element.getAttribute("spellcheck") ?? "unset"\n );\n element.setAttribute("spellcheck", spellcheck);\n }\n var SPELL_CHECK_QUERY = "[contenteditable]:not([contenteditable=false]):not([spellcheck=false]), input:not([spellcheck=false]), textarea:not([spellcheck=false])";\n function disableSpellCheck(document2) {\n const inputs = document2.querySelectorAll(SPELL_CHECK_QUERY);\n inputs.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n setAndBackupSpellcheck(element, "false");\n });\n }\n function restoreSpellCheck(document2) {\n const inputs = document2.querySelectorAll(SPELL_CHECK_QUERY);\n inputs.forEach((input) => {\n const spellcheck = input.getAttribute("data-argos-bck-spellcheck");\n if (spellcheck === "unset") {\n input.removeAttribute("spellcheck");\n } else if (spellcheck) {\n input.setAttribute("spellcheck", spellcheck);\n }\n input.removeAttribute("data-argos-bck-spellcheck");\n });\n }\n function injectGlobalStyles(document2, css, id) {\n const style = document2.createElement("style");\n style.textContent = css;\n style.id = id;\n document2.head.appendChild(style);\n }\n function removeGlobalStyles(document2, id) {\n const style = document2.getElementById(id);\n if (style) {\n style.remove();\n }\n }\n var checkIsHTMLElement = (element) => {\n return "style" in element;\n };\n function setAndBackupPosition(element, position) {\n const previousPosition = element.style.position;\n const previousRect = element.getBoundingClientRect();\n element.style.position = position;\n const currentRect = element.getBoundingClientRect();\n if (previousRect.x !== currentRect.x || previousRect.y !== currentRect.y) {\n element.style.position = previousPosition;\n return;\n }\n element.setAttribute("data-argos-bck-position", previousPosition ?? "unset");\n }\n function stabilizeElementPositions(document2) {\n const window2 = document2.defaultView;\n if (!window2) {\n return;\n }\n const elements = Array.from(document2.querySelectorAll("*"));\n elements.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n if (element.tagName === "IFRAME") {\n return;\n }\n const style = window2.getComputedStyle(element);\n const position = style.position;\n if (position === "fixed") {\n setAndBackupPosition(element, "absolute");\n } else if (position === "sticky") {\n setAndBackupPosition(element, "relative");\n }\n });\n }\n function restoreElementPositions(document2) {\n const window2 = document2.defaultView;\n if (!window2) {\n return;\n }\n const elements = Array.from(document2.querySelectorAll("*"));\n elements.forEach((element) => {\n if (!checkIsHTMLElement(element)) {\n return;\n }\n const position = element.getAttribute("data-argos-bck-position");\n if (position === "unset") {\n element.style.removeProperty("position");\n } else if (position) {\n element.style.position = position;\n }\n element.removeAttribute("data-argos-bck-position");\n });\n }\n function stabilizeImageSizes(document2) {\n const images = Array.from(document2.images);\n const results = images.map((img) => {\n if (img.dataset.argosStabilization === "pending") {\n return false;\n }\n if (img.dataset.argosStabilization === "complete") {\n return true;\n }\n img.dataset.argosStabilization = "pending";\n const originalSrcSet = img.srcset;\n const originalSrc = img.src;\n img.srcset = "";\n img.src = "";\n img.srcset = originalSrcSet;\n img.src = originalSrc;\n const update = () => {\n img.dataset.argosBckWidth = img.style.width ?? "";\n img.dataset.argosBckHeight = img.style.height ?? "";\n const rect = img.getBoundingClientRect();\n img.style.width = `${Math.round(rect.width)}px`;\n img.style.height = `${Math.round(rect.height)}px`;\n img.dataset.argosStabilization = "complete";\n img.removeEventListener("load", update);\n img.removeEventListener("error", update);\n };\n if (img.complete) {\n update();\n return true;\n }\n img.addEventListener("load", update);\n img.addEventListener("error", update);\n return false;\n });\n return results.every((x) => x);\n }\n function addGlobalClass(document2, className) {\n document2.documentElement.classList.add(className);\n }\n function removeGlobalClass(document2, className) {\n document2.documentElement.classList.remove(className);\n }\n function setup(document2, { fullPage, argosCSS } = {}) {\n addGlobalClass(document2, "__argos__");\n injectGlobalStyles(document2, GLOBAL_CSS, "argos-reset-style");\n if (argosCSS) {\n injectGlobalStyles(document2, argosCSS, "argos-user-style");\n }\n disableSpellCheck(document2);\n if (fullPage) {\n stabilizeElementPositions(document2);\n }\n }\n function teardown(document2, { fullPage, argosCSS } = {}) {\n removeGlobalClass(document2, "__argos__");\n removeGlobalStyles(document2, "argos-reset-style");\n if (argosCSS) {\n removeGlobalStyles(document2, "argos-user-style");\n }\n restoreSpellCheck(document2);\n if (fullPage) {\n restoreElementPositions(document2);\n }\n }\n function waitForFontsToLoad(document2) {\n return document2.fonts.status === "loaded";\n }\n function waitForImagesToLoad(document2) {\n const images = Array.from(document2.images);\n return images.every((img) => {\n if (img.decoding !== "sync") {\n img.decoding = "sync";\n }\n if (img.loading !== "eager") {\n img.loading = "eager";\n }\n return img.complete;\n });\n }\n function waitForNoBusy(document2) {\n const checkIsVisible = (element) => {\n if (element instanceof HTMLElement && (element.offsetHeight !== 0 || element.offsetWidth !== 0)) {\n return true;\n }\n return element.getClientRects().length > 0;\n };\n const elements = Array.from(document2.querySelectorAll(\'[aria-busy="true"]\'));\n return elements.every((element) => !checkIsVisible(element));\n }\n function getStabilityState(document2, options) {\n const {\n ariaBusy = true,\n images = true,\n fonts = true,\n imageSizes = true\n } = options ?? {};\n const imagesLoaded = images || imageSizes ? waitForImagesToLoad(document2) : false;\n return {\n ariaBusy: ariaBusy ? waitForNoBusy(document2) : true,\n images: images ? imagesLoaded : true,\n imageSizes: imageSizes ? imagesLoaded ? stabilizeImageSizes(document2) : false : true,\n fonts: fonts ? waitForFontsToLoad(document2) : true\n };\n }\n var VALIDATION_ERRORS = {\n ariaBusy: "Some elements still have `aria-busy=\'true\'`",\n images: "Some images are still loading",\n imageSizes: "Failed to stabilize image sizes",\n fonts: "Some fonts are still loading"\n };\n function getStabilityFailureReasons(document2, options) {\n const stabilityState = getStabilityState(document2, options);\n return Object.entries(stabilityState).reduce(\n (reasons, [key, value]) => {\n if (!value) {\n reasons.push(VALIDATION_ERRORS[key]);\n }\n return reasons;\n },\n []\n );\n }\n function checkIsStable(document2, options) {\n const stabilityState = getStabilityState(document2, options);\n return Object.values(stabilityState).every(Boolean);\n }\n\n // src/global/media.ts\n function getColorScheme(window2) {\n return window2.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";\n }\n function getMediaType(window2) {\n return window2.matchMedia("print").matches ? "print" : "screen";\n }\n\n // src/global/index.ts\n var ArgosGlobal = {\n checkIsStable: (options) => checkIsStable(document, options),\n getStabilityFailureReasons: (options) => getStabilityFailureReasons(document, options),\n afterEach: () => teardown(document),\n setup: (options = {}) => setup(document, options),\n teardown: (options = {}) => teardown(document, options),\n getColorScheme: () => getColorScheme(window),\n getMediaType: () => getMediaType(window)\n };\n window.__ARGOS__ = ArgosGlobal;\n})();\n'; | ||
} | ||
@@ -47,0 +47,0 @@ export { |
{ | ||
"name": "@argos-ci/browser", | ||
"description": "Browser utilities to stabilize visual testing with Argos.", | ||
"version": "3.1.1", | ||
"version": "3.1.2", | ||
"author": "Smooth Code", | ||
@@ -44,3 +44,3 @@ "license": "MIT", | ||
}, | ||
"gitHead": "40ece1cf28bcd480b3d749aafd2945b224eba739" | ||
"gitHead": "10a2ccddf43fa1fced8f8625d24b1d3f19e4096c" | ||
} |
26838
-2.13%481
-1.84%