((resolve) => {
+ imageRequested = resolve;
+ });
+
+ await context.route(/flagged-background\.png/, async (route) => {
+ imageRequested();
+ await imageGate;
+ await route.fulfill({
+ status: 200,
+ contentType: "image/png",
+ body: PNG,
+ });
+ });
+
+ await page.goto(fixture("background-image.html"), {
+ waitUntil: "domcontentloaded",
+ });
+
+ let resolved = false;
+ // No `waitForBackgroundImages` option: the attribute alone opts the
+ // element in.
+ const screenshotPromise = argosScreenshot(
+ page,
+ "background-images-flagged",
+ {
+ fullPage: false,
+ },
+ )
+ .then(() => {
+ resolved = true;
+ })
+ .catch(() => {
+ // Swallow so a failure surfaces through the assertions below.
+ });
+
+ // The plugin must have triggered the flagged image request…
+ await imageRequestedPromise;
+ // …and stabilization must still be blocked on it (well past the other
+ // stabilizers: the 1000ms loader and the 500ms delayed fonts).
+ await page.waitForTimeout(2000);
+ expect(resolved).toBe(false);
+
+ // Once the image loads, stabilization completes.
+ releaseImage();
+ await screenshotPromise;
+ expect(resolved).toBe(true);
+ });
});
test.describe("with `pauseGifs`", () => {
- test.beforeEach(async ({ page }) => {
+ // The 2-frame animated GIF (frame 0 red, frame 1 lime) from the fixture,
+ // served from an extension-less URL so only `data-image-type="gif"` can
+ // flag it.
+ const GIF = Buffer.from(
+ "R0lGODlhZABkAPAAAP8AAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQAFAAAACwAAAAAZABkAAACc4SPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImKi4yNjo+AhpWAAAIfkEABQAAAAsAAAAAGQAZACAAP8AAAAAAnOEj6nL7Q+jnLTai7PevPsPhuJIluaJpurKtu4Lx/JM1/aN5/rO9/4PDAqHxKLxiEwql8ym8wmNSqfUqvWKzWq33K73Cw6Lx+Sy+YxOq9fstvsNj8vn9Lr9js/r9/y+/w8YKDhIWGh4iJiouMjY6PgIaVgAADs=",
+ "base64",
+ );
+
+ test.beforeEach(async ({ page, context }) => {
+ await context.route(/masked-gif-endpoint/, (route) =>
+ route.fulfill({ status: 200, contentType: "image/gif", body: GIF }),
+ );
await page.goto(fixture("gif.html"));
});
@@ -338,6 +411,31 @@ test.describe("#argosScreenshot", () => {
expect(await gif.getAttribute("src")).toBe(originalSrc);
});
+ test('pauses GIFs flagged with `data-image-type="gif"`', async ({
+ page,
+ }) => {
+ const gif = page.locator("#masked-gif");
+
+ // The URL has no `.gif` extension, so only the attribute can flag it.
+ expect(await gif.getAttribute("src")).toBe("masked-gif-endpoint");
+
+ await argosScreenshot(page, "with-masked-gif", { fullPage: false });
+
+ await page.evaluate(() => (window as any).__ARGOS__.beforeEach({}));
+ await page.waitForFunction(() => (window as any).__ARGOS__.waitFor({}));
+ await page.waitForFunction(() => {
+ const img = document.getElementById("masked-gif") as HTMLImageElement;
+ return img.src.startsWith("data:image/png") && img.complete;
+ });
+
+ // The animated GIF is now a static PNG snapshot, frozen on the red frame.
+ expect(await gif.getAttribute("src")).toMatch(/^data:image\/png/);
+
+ // Cleanup restores the original GIF (as a resolved absolute URL).
+ await page.evaluate(() => (window as any).__ARGOS__.afterEach());
+ expect(await gif.getAttribute("src")).toMatch(/masked-gif-endpoint$/);
+ });
+
test("does not pause GIFs when disabled", async ({ page }) => {
await argosScreenshot(page, "with-gif-disabled", { fullPage: false });
diff --git a/packages/playwright/fixtures/background-image.html b/packages/playwright/fixtures/background-image.html
index 3c53be60..dcfa7fa0 100644
--- a/packages/playwright/fixtures/background-image.html
+++ b/packages/playwright/fixtures/background-image.html
@@ -34,6 +34,12 @@
background-image: url("slow-background.png");
background-size: cover;
}
+ #flagged-background-image {
+ width: 200px;
+ height: 100px;
+ background-image: url("flagged-background.png");
+ background-size: cover;
+ }
@@ -48,6 +54,9 @@ Background image page
The font is loaded before the screenshot.
+
+
+