diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 1abf3f40fa..e95a718645 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -343,7 +343,6 @@ exports[`integration tests [html file]: picture.html 1`] = ` \\"This - \\"CORS " `; diff --git a/packages/rrweb-snapshot/test/html/picture.html b/packages/rrweb-snapshot/test/html/picture.html index 3d59df428b..2401ca0c61 100644 --- a/packages/rrweb-snapshot/test/html/picture.html +++ b/packages/rrweb-snapshot/test/html/picture.html @@ -6,6 +6,5 @@ This is a robot - CORS restricted but has access-control-allow-origin: * diff --git a/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png b/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png new file mode 100644 index 0000000000..561f9060d7 Binary files /dev/null and b/packages/rrweb-snapshot/test/images/rrweb-favicon-20x20.png differ diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index cd6cdbdcdb..05f61787b0 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -6,7 +6,7 @@ import * as puppeteer from 'puppeteer'; import * as rollup from 'rollup'; import * as typescript from 'rollup-plugin-typescript2'; import * as assert from 'assert'; -import { waitForRAF } from './utils'; +import { waitForRAF, getServerURL } from './utils'; import { setTimeout } from 'node:timers/promises'; const _typescript = typescript as unknown as () => rollup.Plugin; @@ -210,7 +210,7 @@ iframe.contentDocument.querySelector('center').clientHeight inlineImages: true, inlineStylesheet: false })`); - await waitForRAF(page); // need a small wait, as after the crossOrigin="anonymous" change, the snapshot triggers a reload of the image (which mutates the snapshot when loaded) + // don't wait, as we want to ensure that the same-origin image can be inlined immediately const bodyChildren = (await page.evaluate(` snapshot.childNodes[0].childNodes[1].childNodes.filter((cn) => cn.type === 2); `)) as any[]; @@ -224,11 +224,44 @@ iframe.contentDocument.querySelector('center').clientHeight }, }), ); - expect(bodyChildren[2]).toEqual( + }); + + it('correctly saves cross-origin images offline', async () => { + const page: puppeteer.Page = await browser.newPage(); + + await page.goto('about:blank', { + waitUntil: 'load', + }); + await page.setContent( + ` + + + CORS restricted but has access-control-allow-origin: * + + +`, + { + waitUntil: 'load', + }, + ); + + await page.waitForSelector('img', { timeout: 1000 }); + await page.evaluate(`${code}var snapshot = rrweb.snapshot(document, { + dataURLOptions: { type: "image/webp", quality: 0.8 }, + inlineImages: true, + inlineStylesheet: false + })`); + await waitForRAF(page); // need a small wait, as after the crossOrigin="anonymous" change, the snapshot triggers a reload of the image (after which, the snapshot is mutated) + const bodyChildren = (await page.evaluate(` + snapshot.childNodes[0].childNodes[1].childNodes.filter((cn) => cn.type === 2); +`)) as any[]; + expect(bodyChildren[0]).toEqual( expect.objectContaining({ tagName: 'img', attributes: { - src: 'https://avatars.githubusercontent.com/u/43396833?s=20&v=4', + src: getServerURL(server) + '/images/rrweb-favicon-20x20.png', alt: 'CORS restricted but has access-control-allow-origin: *', rr_dataURL: expect.stringMatching(/^data:image\/webp;base64,/), }, diff --git a/packages/rrweb-snapshot/test/utils.ts b/packages/rrweb-snapshot/test/utils.ts index 43d4484bb4..631f8640a6 100644 --- a/packages/rrweb-snapshot/test/utils.ts +++ b/packages/rrweb-snapshot/test/utils.ts @@ -1,4 +1,5 @@ import * as puppeteer from 'puppeteer'; +import * as http from 'http'; export async function waitForRAF(page: puppeteer.Page) { return await page.evaluate(() => { @@ -9,3 +10,12 @@ export async function waitForRAF(page: puppeteer.Page) { }); }); } + +export function getServerURL(server: http.Server): string { + const address = server.address(); + if (address && typeof address !== 'string') { + return `http://localhost:${address.port}`; + } else { + return `${address}`; + } +}