From f3cf0928df30d5ed5c0d573c524be6e744c0f8d3 Mon Sep 17 00:00:00 2001 From: Alfie Jones Date: Tue, 4 Jun 2024 12:03:23 +0100 Subject: [PATCH] Removing global document references (#1482) fix for options `recordCanvas: true`: * replace document.createElement with doc.createElement in rrweb-snapshot code * Eoghan: add a regression test to prevent future accidental use of `document` instead of `doc`. This test can be excised if a new feature can only be run in the browser and not in the jsdom environment --- .changeset/proud-clocks-hope.md | 5 ++++ packages/rrweb-snapshot/src/rebuild.ts | 2 +- packages/rrweb-snapshot/src/snapshot.ts | 2 +- packages/rrweb-snapshot/test/snapshot.test.ts | 25 +++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .changeset/proud-clocks-hope.md diff --git a/.changeset/proud-clocks-hope.md b/.changeset/proud-clocks-hope.md new file mode 100644 index 00000000..692b2081 --- /dev/null +++ b/.changeset/proud-clocks-hope.md @@ -0,0 +1,5 @@ +--- +"rrweb-snapshot": patch +--- + +(when `recordCanvas: true`): ensure we use doc.createElement instead of document.createElement to allow use in non-browser e.g. jsdom environments diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 7c6ed948..2cb554cb 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -294,7 +294,7 @@ function buildNode( const value = specialAttributes[name]; // handle internal attributes if (tagName === 'canvas' && name === 'rr_dataURL') { - const image = document.createElement('img'); + const image = doc.createElement('img'); image.onload = () => { const ctx = (node as HTMLCanvasElement).getContext('2d'); if (ctx) { diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index 5a2eaa74..6fbfff7b 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -726,7 +726,7 @@ function serializeElementNode( ); // create blank canvas of same dimensions - const blankCanvas = document.createElement('canvas'); + const blankCanvas = doc.createElement('canvas'); blankCanvas.width = (n as HTMLCanvasElement).width; blankCanvas.height = (n as HTMLCanvasElement).height; const blankCanvasDataURL = blankCanvas.toDataURL( diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index aa4bb428..7c930c39 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -7,6 +7,7 @@ import { serializeNodeWithId, _isBlockedElement, } from '../src/snapshot'; +import snapshot from '../src/snapshot'; import { serializedNodeWithId, elementNode } from '../src/types'; import { Mirror } from '../src/utils'; @@ -257,3 +258,27 @@ describe('form', () => { expect(sel?.childNodes).toEqual([]); // shouldn't be stored in childNodes while in transit }); }); + +describe('jsdom snapshot', () => { + const render = (html: string): Document => { + document.write(html); + return document; + }; + + it("doesn't rely on global browser objects", () => { + // this test is incomplete in terms of coverage, + // but the idea being that we are checking that all features use the + // passed-in `doc` object rather than the global `document` + // (which is only present in browsers) + // in any case, supporting jsdom is not a primary goal + + const doc = render(`

Hello world

`); + const sn = snapshot(doc, { + // JSDOM Error: Not implemented: HTMLCanvasElement.prototype.toDataURL (without installing the canvas npm package) + //recordCanvas: true, + }); + expect(sn).toMatchObject({ + type: 0, + }); + }); +});