Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(replays): Add snapshot function to replay canvas integration #10066

Merged
merged 50 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
64a84af
feat(replay): Add `ReplayCanvas` integration
mydea Jan 4, 2024
f9507ef
refactor out integrations abstraction
billyvg Jan 10, 2024
1e91917
update
c298lee Jan 11, 2024
e1ec42e
it works
c298lee Jan 11, 2024
16d99e6
add manual snapshot option
c298lee Jan 12, 2024
f74f12b
add manual snapshot option
c298lee Jan 12, 2024
fd5c9e6
feat(replay): Add `ReplayCanvas` integration
mydea Jan 4, 2024
1b2b670
new quality options
billyvg Jan 4, 2024
1b21a37
fix
billyvg Jan 5, 2024
446aea7
create replay-canvas package
billyvg Jan 8, 2024
f9a887d
cleanup repo
billyvg Jan 9, 2024
32b4ccd
change to not use `_experiments` for canvas
billyvg Jan 9, 2024
255fa86
revert entrypoint change
billyvg Jan 9, 2024
425f1ab
move tests to dev-packages
billyvg Jan 9, 2024
97fa4e7
update tests
billyvg Jan 9, 2024
cd8f159
remove old template
billyvg Jan 10, 2024
eedcf21
do not publish as separate package
billyvg Jan 10, 2024
f103983
remove yarn lockfile
billyvg Jan 10, 2024
c4e957f
refactor out integrations abstraction
billyvg Jan 10, 2024
fbe85d5
add replaycanvas to replay bundles
billyvg Jan 10, 2024
20e61fd
forgot shims
billyvg Jan 10, 2024
645bbbb
debug playwright test
billyvg Jan 10, 2024
6e51bff
Revert "debug playwright test"
billyvg Jan 11, 2024
26d834e
fix types
billyvg Jan 11, 2024
fed7da2
lint
billyvg Jan 11, 2024
5aa96e3
Revert "add replaycanvas to replay bundles"
billyvg Jan 15, 2024
6d5a0f2
Revert "forgot shims"
billyvg Jan 15, 2024
833ee6f
add `canvas` to options event
billyvg Jan 16, 2024
1f790e2
move replay-canvas to devDeps
billyvg Jan 16, 2024
1121e89
canvas --> shouldRecordCanvas
billyvg Jan 16, 2024
e805e6c
move back to deps
billyvg Jan 16, 2024
ec1bbe9
remove private: true, fails the e2e tests
billyvg Jan 16, 2024
7ee6be0
feat(replay): Add `ReplayCanvas` integration
mydea Jan 4, 2024
0f32f8a
refactor out integrations abstraction
billyvg Jan 10, 2024
3a5ea02
Merge branch 'fn/replay-canvas-integration-sep-package' into snapshot…
c298lee Jan 11, 2024
9a09eff
add snapshot to shim
c298lee Jan 12, 2024
d3f23cc
lint
c298lee Jan 12, 2024
43daae2
test and snapshot
c298lee Jan 15, 2024
0a1350c
fix
c298lee Jan 15, 2024
8db7bde
fix again
c298lee Jan 16, 2024
6324d18
done fix hopefully
c298lee Jan 16, 2024
b8a8a3a
comments and update test
c298lee Jan 16, 2024
8807086
typo
c298lee Jan 16, 2024
b3f5889
typo
c298lee Jan 16, 2024
64e0a5b
lint
c298lee Jan 16, 2024
9f769b4
update enableManualSnapshot
c298lee Jan 16, 2024
bdb58a4
fix merge
c298lee Jan 16, 2024
7382f97
fix merge and change rrweb to 2.9.0
c298lee Jan 17, 2024
3d6df92
lint
c298lee Jan 17, 2024
6e6b808
Merge branch 'develop' into snapshot-replay-canvas
c298lee Jan 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev-packages/browser-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"@playwright/test": "^1.31.1",
"@sentry-internal/rrweb": "2.8.0",
"@sentry-internal/rrweb": "2.9.0",
"@sentry/browser": "7.93.0",
"@sentry/tracing": "7.93.0",
"axios": "1.6.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;
window.Replay = new Sentry.Replay({
flushMinDelay: 50,
flushMaxDelay: 50,
minReplayDuration: 0,
});

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
sampleRate: 0,
replaysSessionSampleRate: 1.0,
replaysOnErrorSampleRate: 0.0,
debug: true,

integrations: [window.Replay, new Sentry.ReplayCanvas({ enableManualSnapshot: true })],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="canvas" width="150" height="150"></canvas>
<button id="draw">Draw</button>
</body>


<script>
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
console.log('has canvas')
const ctx = canvas.getContext("2d");

ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
}
document.getElementById('draw').addEventListener('click', draw);
</script>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { expect } from '@playwright/test';

import { sentryTest } from '../../../../utils/fixtures';
import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers';

sentryTest('can manually snapshot canvas', async ({ getLocalTestUrl, page, browserName }) => {
if (shouldSkipReplayTest() || browserName === 'webkit' || (process.env.PW_BUNDLE || '').startsWith('bundle')) {
sentryTest.skip();
}

const reqPromise0 = waitForReplayRequest(page, 0);
const reqPromise1 = waitForReplayRequest(page, 1);
const reqPromise2 = waitForReplayRequest(page, 2);
const reqPromise3 = waitForReplayRequest(page, 3);

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);
expect(incrementalSnapshots).toEqual([]);

await page.evaluate(() => {
(window as any).Sentry.getClient().getIntegrationById('ReplayCanvas').snapshot();
});

const { incrementalSnapshots: incrementalSnapshotsManual } = getReplayRecordingContent(await reqPromise3);
expect(incrementalSnapshotsManual).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',
type: 'image/webp',
},
],
rr_type: 'ImageBitmap',
},
0,
0,
],
property: 'drawImage',
},
],
id: 9,
source: 9,
type: 0,
},
timestamp: 0,
type: 3,
},
]),
);
});
2 changes: 1 addition & 1 deletion packages/replay-canvas/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"homepage": "https://docs.sentry.io/platforms/javascript/session-replay/",
"devDependencies": {
"@babel/core": "^7.17.5",
"@sentry-internal/rrweb": "2.8.0"
"@sentry-internal/rrweb": "2.9.0"
},
"dependencies": {
"@sentry/core": "7.93.0",
Expand Down
19 changes: 17 additions & 2 deletions packages/replay-canvas/src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import type { CanvasManagerInterface, CanvasManagerOptions } from '@sentry/repla
import type { Integration, IntegrationClass, IntegrationFn } from '@sentry/types';

interface ReplayCanvasOptions {
enableManualSnapshot?: boolean;
quality: 'low' | 'medium' | 'high';
}

type GetCanvasManager = (options: CanvasManagerOptions) => CanvasManagerInterface;
export interface ReplayCanvasIntegrationOptions {
enableManualSnapshot?: boolean;
recordCanvas: true;
getCanvasManager: GetCanvasManager;
sampling: {
Expand Down Expand Up @@ -58,21 +60,34 @@ const INTEGRATION_NAME = 'ReplayCanvas';
const replayCanvasIntegration = ((options: Partial<ReplayCanvasOptions> = {}) => {
const _canvasOptions = {
quality: options.quality || 'medium',
enableManualSnapshot: options.enableManualSnapshot,
};

let canvasManagerResolve: (value: CanvasManager) => void;
const _canvasManager: Promise<CanvasManager> = new Promise(resolve => (canvasManagerResolve = resolve));

return {
name: INTEGRATION_NAME,
// eslint-disable-next-line @typescript-eslint/no-empty-function
setupOnce() {},
getOptions(): ReplayCanvasIntegrationOptions {
const { quality } = _canvasOptions;
const { quality, enableManualSnapshot } = _canvasOptions;

return {
enableManualSnapshot,
recordCanvas: true,
getCanvasManager: (options: CanvasManagerOptions) => new CanvasManager(options),
getCanvasManager: (options: CanvasManagerOptions) => {
const manager = new CanvasManager({ ...options, enableManualSnapshot });
canvasManagerResolve(manager);
return manager;
},
...(CANVAS_QUALITY[quality || 'medium'] || CANVAS_QUALITY.medium),
};
},
async snapshot(canvasElement?: HTMLCanvasElement) {
c298lee marked this conversation as resolved.
Show resolved Hide resolved
const canvasManager = await _canvasManager;
canvasManager.snapshot(canvasElement);
},
};
}) satisfies IntegrationFn;

Expand Down
5 changes: 3 additions & 2 deletions packages/replay-canvas/test/canvas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ it('initializes with default options', () => {
});
});

it('initializes with quality option', () => {
const rc = new ReplayCanvas({ quality: 'low' });
it('initializes with quality option and manual snapshot', () => {
const rc = new ReplayCanvas({ enableManualSnapshot: true, quality: 'low' });

expect(rc.getOptions()).toEqual({
enableManualSnapshot: true,
recordCanvas: true,
getCanvasManager: expect.any(Function),
sampling: {
Expand Down
4 changes: 2 additions & 2 deletions packages/replay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
"devDependencies": {
"@babel/core": "^7.17.5",
"@sentry-internal/replay-worker": "7.93.0",
"@sentry-internal/rrweb": "2.8.0",
"@sentry-internal/rrweb-snapshot": "2.8.0",
"@sentry-internal/rrweb": "2.9.0",
"@sentry-internal/rrweb-snapshot": "2.9.0",
"fflate": "^0.8.1",
"jsdom-worker": "^0.2.1"
},
Expand Down
1 change: 1 addition & 0 deletions packages/replay/src/types/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ export interface SlowClickConfig {
}

export interface ReplayCanvasIntegrationOptions {
enableManualSnapshot?: boolean;
recordCanvas: true;
getCanvasManager: (options: CanvasManagerOptions) => CanvasManagerInterface;
sampling: {
Expand Down
1 change: 1 addition & 0 deletions packages/replay/src/types/rrweb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface CanvasManagerInterface {

export interface CanvasManagerOptions {
recordCanvas: boolean;
enableManualSnapshot?: boolean;
blockClass: string | RegExp;
blockSelector: string | null;
unblockSelector: string | null;
Expand Down