mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-16 15:45:13 +00:00
Stabilize visual tests (#3001)
This commit is contained in:
@@ -191,21 +191,20 @@ export function runTestCases(testCases: TestsCase[]) {
|
||||
.intercom-lightweight-app {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Switch image rendering to pixelated */
|
||||
img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
`,
|
||||
threshold: screenshotOptions?.threshold ?? undefined,
|
||||
fullPage: testEntry.fullPage ?? false,
|
||||
beforeScreenshot: async ({ runStabilization }) => {
|
||||
await runStabilization();
|
||||
await waitForIcons(page);
|
||||
await roundImageSizes(page);
|
||||
if (screenshotOptions?.waitForTOCScrolling !== false) {
|
||||
await waitForTOCScrolling(page);
|
||||
}
|
||||
},
|
||||
afterScreenshot: async () => {
|
||||
await restoreImageSizes(page);
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -326,7 +325,14 @@ async function waitForIcons(page: Page) {
|
||||
function loadImage(src: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onload = () => {
|
||||
// Wait two frames to ensure the image has been rendered
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
img.onerror = (_error) => reject(new Error(`Failed to load image: ${src}`));
|
||||
img.src = src;
|
||||
});
|
||||
@@ -348,6 +354,70 @@ async function waitForIcons(page: Page) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Take all images, measure them and set their width and height to rounded values.
|
||||
*/
|
||||
async function roundImageSizes(page: Page) {
|
||||
await page.waitForFunction(async () => {
|
||||
const images = Array.from(document.querySelectorAll('img'));
|
||||
await Promise.all(
|
||||
images.map(async (img) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const setDimensions = () => {
|
||||
// Mark it as stabilized
|
||||
img.dataset.stabilized = 'true';
|
||||
// Preserve the original width and height
|
||||
img.dataset.originalWidth = img.style.width ?? '';
|
||||
img.dataset.originalHeight = img.style.height ?? '';
|
||||
const rect = img.getBoundingClientRect();
|
||||
img.style.width = `${Math.round(rect.width)}px`;
|
||||
img.style.height = `${Math.round(rect.height)}px`;
|
||||
resolve();
|
||||
};
|
||||
|
||||
if (img.complete) {
|
||||
setDimensions();
|
||||
} else {
|
||||
const cleanup = () => {
|
||||
img.removeEventListener('load', handleLoad);
|
||||
img.removeEventListener('error', handleError);
|
||||
};
|
||||
const handleError = () => {
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
const handleLoad = () => {
|
||||
cleanup();
|
||||
setDimensions();
|
||||
};
|
||||
img.addEventListener('load', handleLoad);
|
||||
img.addEventListener('error', handleError);
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore images to their original size.
|
||||
*/
|
||||
async function restoreImageSizes(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
const images = Array.from(document.querySelectorAll('img[data-stabilized]'));
|
||||
images.forEach((img) => {
|
||||
if (img instanceof HTMLImageElement) {
|
||||
img.style.width = img.dataset.originalWidth ?? '';
|
||||
img.style.height = img.dataset.originalHeight ?? '';
|
||||
delete img.dataset.originalWidth;
|
||||
delete img.dataset.originalHeight;
|
||||
delete img.dataset.stabilized;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for TOC to be correctly scrolled into view.
|
||||
*/
|
||||
|
||||
@@ -18,5 +18,8 @@ export default defineConfig({
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
contextOptions: {
|
||||
reducedMotion: 'reduce',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -115,9 +115,15 @@ function Shortcut() {
|
||||
setOperatingSystem(getOperatingSystem());
|
||||
}, []);
|
||||
|
||||
return operatingSystem ? (
|
||||
return (
|
||||
<div
|
||||
className={`shortcut -mr-1 hidden animate-fadeIn justify-end gap-0.5 whitespace-nowrap text-tint text-xs [font-feature-settings:"calt",_"case"] contrast-more:text-tint-strong md:flex`}
|
||||
aria-busy={operatingSystem === null ? 'true' : undefined}
|
||||
className={tcls(
|
||||
`shortcut -mr-1 hidden justify-end gap-0.5 whitespace-nowrap text-tint text-xs [font-feature-settings:"calt",_"case"] contrast-more:text-tint-strong md:flex`,
|
||||
operatingSystem
|
||||
? 'motion-safe:animate-fadeIn motion-reduce:opacity-100'
|
||||
: 'opacity-0'
|
||||
)}
|
||||
>
|
||||
<kbd
|
||||
className={`flex h-5 min-w-5 items-center justify-center rounded border border-tint-subtle theme-bold:border-header-link/5 bg-tint-base theme-bold:bg-header-background px-1 ${operatingSystem === 'mac' ? 'text-sm' : ''}`}
|
||||
@@ -128,7 +134,5 @@ function Shortcut() {
|
||||
K
|
||||
</kbd>
|
||||
</div>
|
||||
) : (
|
||||
<span aria-busy />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -297,8 +297,8 @@ const config: Config = {
|
||||
present: 'present .5s ease-out both',
|
||||
scaleIn: 'scaleIn 200ms ease',
|
||||
scaleOut: 'scaleOut 200ms ease',
|
||||
fadeIn: 'fadeIn 200ms ease',
|
||||
fadeOut: 'fadeOut 200ms ease',
|
||||
fadeIn: 'fadeIn 200ms ease forwards',
|
||||
fadeOut: 'fadeOut 200ms ease forwards',
|
||||
enterFromLeft: 'enterFromLeft 250ms ease',
|
||||
enterFromRight: 'enterFromRight 250ms ease',
|
||||
exitToLeft: 'exitToLeft 250ms ease',
|
||||
|
||||
Reference in New Issue
Block a user