From 9758ff2f3748e90278848af3a576e8013c0e888a Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 5 Jan 2024 10:56:08 -0500 Subject: [PATCH 1/2] test(browser-integration): Add test for Replay canvas recording Adds a test to ensure that Replay canvas recording works. --- .../suites/replay/canvas/records/init.js | 24 ++++++ .../replay/canvas/records/template.html | 26 +++++++ .../suites/replay/canvas/records/test.ts | 74 +++++++++++++++++++ .../suites/replay/canvas/template.html | 9 +++ .../utils/replayHelpers.ts | 7 ++ 5 files changed, 140 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/replay/canvas/records/init.js create mode 100644 dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html create mode 100644 dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/replay/canvas/template.html diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/init.js b/dev-packages/browser-integration-tests/suites/replay/canvas/records/init.js new file mode 100644 index 000000000000..e530d741a8bc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/init.js @@ -0,0 +1,24 @@ +import { getCanvasManager } from '@sentry-internal/rrweb'; +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 50, + flushMaxDelay: 50, + minReplayDuration: 0, + _experiments: { + canvas: { + manager: getCanvasManager, + }, + }, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + debug: true, + + integrations: [window.Replay], +}); diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html b/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html new file mode 100644 index 000000000000..5f23d569fcc2 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/template.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts new file mode 100644 index 000000000000..1c88ae79255a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts @@ -0,0 +1,74 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; + +sentryTest('can record canvas', async ({ getLocalTestUrl, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + const reqPromise1 = waitForReplayRequest(page, 1); + const reqPromise2 = waitForReplayRequest(page, 2); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + await reqPromise0; + await Promise.all([page.click('#draw'), reqPromise1]); + + const { incrementalSnapshots } = getReplayRecordingContent(await reqPromise2); + // console.log(incrementalSnapshots[0]); + expect(incrementalSnapshots).toEqual( + expect.arrayContaining([ + { + data: { + commands: [ + { + args: [0, 0, 150, 150], + property: 'clearRect', + }, + { + args: [ + { + args: [ + { + data: [ + { + base64: expect.any(String), + rr_type: 'ArrayBuffer', + }, + ], + rr_type: 'Blob', + // chrome = webp, safari = png + type: expect.stringMatching(/webp|png/), + }, + ], + rr_type: 'ImageBitmap', + }, + 0, + 0, + ], + property: 'drawImage', + }, + ], + id: 9, + source: 9, + type: 0, + }, + timestamp: 0, + type: 3, + }, + ]), + ); +}); diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/template.html b/dev-packages/browser-integration-tests/suites/replay/canvas/template.html new file mode 100644 index 000000000000..2b3e2f0b27b4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/utils/replayHelpers.ts b/dev-packages/browser-integration-tests/utils/replayHelpers.ts index 613bd5b447f1..2a951e4a215e 100644 --- a/dev-packages/browser-integration-tests/utils/replayHelpers.ts +++ b/dev-packages/browser-integration-tests/utils/replayHelpers.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import type { fullSnapshotEvent, incrementalSnapshotEvent } from '@sentry-internal/rrweb'; import { EventType } from '@sentry-internal/rrweb'; import type { ReplayEventWithTime } from '@sentry/browser'; @@ -5,6 +6,7 @@ import type { InternalEventContext, RecordingEvent, ReplayContainer, + ReplayPluginOptions, Session, } from '@sentry/replay/build/npm/types/types'; import type { Breadcrumb, Event, ReplayEvent, ReplayRecordingMode } from '@sentry/types'; @@ -171,6 +173,8 @@ export function getReplaySnapshot(page: Page): Promise<{ _isPaused: boolean; _isEnabled: boolean; _context: InternalEventContext; + _options: ReplayPluginOptions; + _hasCanvas: boolean; session: Session | undefined; recordingMode: ReplayRecordingMode; }> { @@ -182,6 +186,9 @@ export function getReplaySnapshot(page: Page): Promise<{ _isPaused: replay.isPaused(), _isEnabled: replay.isEnabled(), _context: replay.getContext(), + _options: replay.getOptions(), + // We cannot pass the function through as this is serialized + _hasCanvas: typeof replay.getOptions()._experiments.canvas?.manager === 'function', session: replay.session, recordingMode: replay.recordingMode, }; From 26aebee5b7e373efcb572b489e3376b7560c2253 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 5 Jan 2024 11:04:09 -0500 Subject: [PATCH 2/2] cleanup & skip webkit --- .../suites/replay/canvas/records/test.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts index 1c88ae79255a..372ca8978356 100644 --- a/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts +++ b/dev-packages/browser-integration-tests/suites/replay/canvas/records/test.ts @@ -3,8 +3,8 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; -sentryTest('can record canvas', async ({ getLocalTestUrl, page }) => { - if (shouldSkipReplayTest()) { +sentryTest('can record canvas', async ({ getLocalTestUrl, page, browserName }) => { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -23,12 +23,10 @@ sentryTest('can record canvas', async ({ getLocalTestUrl, page }) => { const url = await getLocalTestUrl({ testDir: __dirname }); await page.goto(url); - await reqPromise0; await Promise.all([page.click('#draw'), reqPromise1]); const { incrementalSnapshots } = getReplayRecordingContent(await reqPromise2); - // console.log(incrementalSnapshots[0]); expect(incrementalSnapshots).toEqual( expect.arrayContaining([ { @@ -50,8 +48,7 @@ sentryTest('can record canvas', async ({ getLocalTestUrl, page }) => { }, ], rr_type: 'Blob', - // chrome = webp, safari = png - type: expect.stringMatching(/webp|png/), + type: 'image/webp', }, ], rr_type: 'ImageBitmap',