diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml
index cd82966591ce..765e1c53615f 100644
--- a/.github/workflows/auto-release.yml
+++ b/.github/workflows/auto-release.yml
@@ -33,6 +33,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GH_RELEASE_PAT }}
with:
- version: ${{ steps.version.outputs.match != '' }}
+ version: ${{ steps.version.outputs.group1 }}
force: false
merge_target: master
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6a9f6372afb9..45aa5f4ec595 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -298,11 +298,14 @@ jobs:
path: ${{ env.CACHED_BUILD_PATHS }}
key: ${{ env.BUILD_CACHE_KEY }}
- name: Check bundle sizes
- uses: getsentry/size-limit-action@main-skip-step
+ uses: getsentry/size-limit-action@runForBranch
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
skip_step: build
main_branch: develop
+ # When on release branch, we want to always run
+ # Else, we fall back to the default handling of the action
+ run_for_branch: ${{ (needs.job_get_metadata.outputs.is_release == 'true' && 'true') || '' }}
job_lint:
name: Lint
diff --git a/packages/integration-tests/README.md b/packages/integration-tests/README.md
index 47aa171cec21..075163a4b9dc 100644
--- a/packages/integration-tests/README.md
+++ b/packages/integration-tests/README.md
@@ -12,6 +12,9 @@ Each case group has a default HTML skeleton named `template.hbs`, and also a def
`test.ts` is required for each test case, which contains the assertions (and if required the script injection logic). For every case, any set of `init.js`, `template.hbs` and `subject.js` can be defined locally, and each one of them will have precedence over the default definitions of the test group.
+To test page multi-page navigations, you can specify additional `page-*.html` (e.g. `page-0.html`, `page-1.html`) files. These will also be compiled and initialized with the same `init.js` and `subject.js` files that are applied to `template.hbs/html`. Note: `page-*.html` file lookup **doesn not** fall back to the
+parent directories, meaning that page files have to be directly in the `test.ts` directory.
+
```
suites/
|---- breadcrumbs/
@@ -23,6 +26,7 @@ suites/
|---- init.js [optional case specific init]
|---- subject.js [optional case specific subject]
|---- test.ts [assertions]
+ |---- page-*.html [optional, NO fallback!]
```
## Writing Tests
diff --git a/packages/integration-tests/suites/replay/multiple-pages/init.js b/packages/integration-tests/suites/replay/multiple-pages/init.js
new file mode 100644
index 000000000000..0242b48100b8
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/init.js
@@ -0,0 +1,16 @@
+import * as Sentry from '@sentry/browser';
+
+window.Sentry = Sentry;
+window.Replay = new Sentry.Replay({
+ flushMinDelay: 500,
+ flushMaxDelay: 500,
+});
+
+Sentry.init({
+ dsn: 'https://public@dsn.ingest.sentry.io/1337',
+ sampleRate: 0,
+ replaysSessionSampleRate: 1.0,
+ replaysOnErrorSampleRate: 0,
+
+ integrations: [window.Replay],
+});
diff --git a/packages/integration-tests/suites/replay/multiple-pages/page-0.html b/packages/integration-tests/suites/replay/multiple-pages/page-0.html
new file mode 100644
index 000000000000..f6cb8d50a131
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/page-0.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Secondary Page
+ New Tab
+ Mimic a SPA navigation
+
+ Go Back to first page
+
+
diff --git a/packages/integration-tests/suites/replay/multiple-pages/subject.js b/packages/integration-tests/suites/replay/multiple-pages/subject.js
new file mode 100644
index 000000000000..0f867559c98b
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/subject.js
@@ -0,0 +1,10 @@
+document.getElementById('go-background').addEventListener('click', () => {
+ Object.defineProperty(document, 'hidden', { value: true, writable: true });
+ const ev = document.createEvent('Event');
+ ev.initEvent('visibilitychange');
+ document.dispatchEvent(ev);
+});
+
+document.getElementById('spa-navigation').addEventListener('click', () => {
+ window.history.pushState({}, '', '/spa');
+});
diff --git a/packages/integration-tests/suites/replay/multiple-pages/template.html b/packages/integration-tests/suites/replay/multiple-pages/template.html
new file mode 100644
index 000000000000..12bc2cc67ba0
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/template.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ New Tab
+ Go To new page
+
+
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts b/packages/integration-tests/suites/replay/multiple-pages/test.ts
new file mode 100644
index 000000000000..914c7dd856ca
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts
@@ -0,0 +1,332 @@
+import { expect } from '@playwright/test';
+
+import { sentryTest } from '../../../utils/fixtures';
+import {
+ expectedClickBreadcrumb,
+ expectedFCPPerformanceSpan,
+ expectedFPPerformanceSpan,
+ expectedLCPPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ expectedNavigationBreadcrumb,
+ expectedNavigationPerformanceSpan,
+ expectedNavigationPushPerformanceSpan,
+ expectedReloadPerformanceSpan,
+ getExpectedReplayEvent,
+} from '../../../utils/replayEventTemplates';
+import {
+ getReplayEvent,
+ getReplayRecordingContent,
+ normalize,
+ shouldSkipReplayTest,
+ waitForReplayRequest,
+} from '../../../utils/replayHelpers';
+
+/*
+This is a quite complex test with the goal to ensure correct recording across multiple pages,
+navigations and page reloads. In particular, we want to check that all breadcrumbs, spans as
+well as the correct DOM snapshots and updates are recorded and sent.
+*/
+sentryTest(
+ 'record page navigations and performance entries across multiple pages',
+ async ({ getLocalTestPath, page, browserName }) => {
+ // We only test this against the NPM package and replay bundles
+ // and only on chromium as most performance entries are only available in chromium
+ if (shouldSkipReplayTest() || browserName !== 'chromium') {
+ sentryTest.skip();
+ }
+
+ await page.route('https://dsn.ingest.sentry.io/**/*', route => {
+ return route.fulfill({
+ status: 200,
+ contentType: 'application/json',
+ body: JSON.stringify({ id: 'test-id' }),
+ });
+ });
+
+ const reqPromise0 = waitForReplayRequest(page, 0);
+ const reqPromise1 = waitForReplayRequest(page, 1);
+
+ const url = await getLocalTestPath({ testDir: __dirname });
+
+ await page.goto(url);
+ const replayEvent0 = getReplayEvent(await reqPromise0);
+ const recording0 = getReplayRecordingContent(await reqPromise0);
+
+ expect(replayEvent0).toEqual(getExpectedReplayEvent({ segment_id: 0 }));
+ expect(normalize(recording0.fullSnapshots)).toMatchSnapshot('seg-0-snap-full');
+ expect(recording0.incrementalSnapshots.length).toEqual(0);
+
+ await page.click('#go-background');
+
+ const replayEvent1 = getReplayEvent(await reqPromise1);
+ const recording1 = getReplayRecordingContent(await reqPromise1);
+
+ expect(replayEvent1).toEqual(
+ getExpectedReplayEvent({ segment_id: 1, urls: [], replay_start_timestamp: undefined }),
+ );
+ expect(recording1.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording1.incrementalSnapshots)).toMatchSnapshot('seg-1-snap-incremental');
+
+ // We can't guarantee the order of the performance spans, or in which of the two segments they are sent
+ // So to avoid flakes, we collect them all and check that they are all there
+ const collectedPerformanceSpans = [...recording0.performanceSpans, ...recording1.performanceSpans];
+ const collectedBreadcrumbs = [...recording0.breadcrumbs, ...recording1.breadcrumbs];
+
+ expect(collectedPerformanceSpans.length).toEqual(6);
+ expect(collectedPerformanceSpans).toEqual(
+ expect.arrayContaining([
+ expectedNavigationPerformanceSpan,
+ expectedLCPPerformanceSpan,
+ expectedFPPerformanceSpan,
+ expectedFCPPerformanceSpan,
+ expectedMemoryPerformanceSpan, // two memory spans - once per flush
+ expectedMemoryPerformanceSpan,
+ ]),
+ );
+
+ expect(collectedBreadcrumbs).toEqual([expectedClickBreadcrumb]);
+
+ // -----------------------------------------------------------------------------------------
+ // Test page reload
+
+ await page.reload();
+
+ const reqPromise2 = waitForReplayRequest(page, 2);
+ const reqPromise3 = waitForReplayRequest(page, 3);
+
+ const replayEvent2 = getReplayEvent(await reqPromise2);
+ const recording2 = getReplayRecordingContent(await reqPromise2);
+
+ expect(replayEvent2).toEqual(getExpectedReplayEvent({ segment_id: 2, replay_start_timestamp: undefined }));
+ expect(normalize(recording2.fullSnapshots)).toMatchSnapshot('seg-2-snap-full');
+ expect(recording2.incrementalSnapshots.length).toEqual(0);
+
+ await page.click('#go-background');
+
+ const replayEvent3 = getReplayEvent(await reqPromise3);
+ const recording3 = getReplayRecordingContent(await reqPromise3);
+
+ expect(replayEvent3).toEqual(
+ getExpectedReplayEvent({ segment_id: 3, urls: [], replay_start_timestamp: undefined }),
+ );
+ expect(recording3.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording3.incrementalSnapshots)).toMatchSnapshot('seg-3-snap-incremental');
+
+ const collectedPerformanceSpansAfterReload = [...recording2.performanceSpans, ...recording3.performanceSpans];
+ const collectedBreadcrumbsAdterReload = [...recording2.breadcrumbs, ...recording3.breadcrumbs];
+
+ expect(collectedPerformanceSpansAfterReload.length).toEqual(6);
+ expect(collectedPerformanceSpansAfterReload).toEqual(
+ expect.arrayContaining([
+ expectedReloadPerformanceSpan,
+ expectedLCPPerformanceSpan,
+ expectedFPPerformanceSpan,
+ expectedFCPPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ ]),
+ );
+
+ expect(collectedBreadcrumbsAdterReload).toEqual([expectedClickBreadcrumb]);
+
+ // -----------------------------------------------------------------------------------------
+ // Test subsequent link navigation to another page
+
+ await page.click('a');
+
+ const reqPromise4 = waitForReplayRequest(page, 4);
+ const reqPromise5 = waitForReplayRequest(page, 5);
+
+ const replayEvent4 = getReplayEvent(await reqPromise4);
+ const recording4 = getReplayRecordingContent(await reqPromise4);
+
+ expect(replayEvent4).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 4,
+ replay_start_timestamp: undefined,
+ // @ts-ignore this is fine
+ urls: [expect.stringContaining('page-0.html')],
+ request: {
+ // @ts-ignore this is fine
+ url: expect.stringContaining('page-0.html'),
+ headers: {
+ // @ts-ignore this is fine
+ 'User-Agent': expect.stringContaining(''),
+ },
+ },
+ }),
+ );
+ expect(normalize(recording4.fullSnapshots)).toMatchSnapshot('seg-4-snap-full');
+ expect(recording4.incrementalSnapshots.length).toEqual(0);
+
+ await page.click('#go-background');
+
+ const replayEvent5 = getReplayEvent(await reqPromise5);
+ const recording5 = getReplayRecordingContent(await reqPromise5);
+
+ expect(replayEvent5).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 5,
+ urls: [],
+ replay_start_timestamp: undefined,
+ request: {
+ // @ts-ignore this is fine
+ url: expect.stringContaining('page-0.html'),
+ headers: {
+ // @ts-ignore this is fine
+ 'User-Agent': expect.stringContaining(''),
+ },
+ },
+ }),
+ );
+ expect(recording5.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording5.incrementalSnapshots)).toMatchSnapshot('seg-5-snap-incremental');
+
+ const collectedPerformanceSpansAfterLinkNavigation = [
+ ...recording4.performanceSpans,
+ ...recording5.performanceSpans,
+ ];
+ const collectedBreadcrumbsAfterLinkNavigation = [...recording4.breadcrumbs, ...recording5.breadcrumbs];
+
+ expect(collectedPerformanceSpansAfterLinkNavigation).toEqual(
+ expect.arrayContaining([
+ expectedNavigationPerformanceSpan,
+ expectedLCPPerformanceSpan,
+ expectedFPPerformanceSpan,
+ expectedFCPPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ ]),
+ );
+
+ expect(collectedBreadcrumbsAfterLinkNavigation.length).toEqual(1);
+ expect(collectedBreadcrumbsAfterLinkNavigation).toEqual([expectedClickBreadcrumb]);
+
+ // -----------------------------------------------------------------------------------------
+ // Test subsequent navigation without a page reload (i.e. SPA navigation)
+
+ await page.click('#spa-navigation');
+
+ const reqPromise6 = waitForReplayRequest(page, 6);
+ const replayEvent6 = getReplayEvent(await reqPromise6);
+ const recording6 = getReplayRecordingContent(await reqPromise6);
+
+ expect(replayEvent6).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 6,
+ urls: ['/spa'],
+ replay_start_timestamp: undefined,
+ request: {
+ // @ts-ignore this is fine
+ url: expect.stringContaining('page-0.html'),
+ headers: {
+ // @ts-ignore this is fine
+ 'User-Agent': expect.stringContaining(''),
+ },
+ },
+ }),
+ );
+ expect(recording6.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording6.incrementalSnapshots)).toMatchSnapshot('seg-6-snap-incremental');
+
+ await page.click('#go-background');
+
+ const reqPromise7 = waitForReplayRequest(page, 7);
+ const replayEvent7 = getReplayEvent(await reqPromise7);
+ const recording7 = getReplayRecordingContent(await reqPromise7);
+
+ expect(replayEvent7).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 7,
+ urls: [],
+ replay_start_timestamp: undefined,
+ request: {
+ // @ts-ignore this is fine
+ url: expect.stringContaining('page-0.html'),
+ headers: {
+ // @ts-ignore this is fine
+ 'User-Agent': expect.stringContaining(''),
+ },
+ },
+ }),
+ );
+ expect(recording7.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording7.incrementalSnapshots)).toMatchSnapshot('seg-7-snap-incremental');
+
+ const collectedPerformanceSpansAfterSPANavigation = [
+ ...recording6.performanceSpans,
+ ...recording7.performanceSpans,
+ ];
+ const collectedBreadcrumbsAfterSPANavigation = [...recording6.breadcrumbs, ...recording7.breadcrumbs];
+
+ expect(collectedPerformanceSpansAfterSPANavigation.length).toEqual(3);
+ expect(collectedPerformanceSpansAfterSPANavigation).toEqual(
+ expect.arrayContaining([
+ expectedNavigationPushPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ ]),
+ );
+
+ expect(collectedBreadcrumbsAfterSPANavigation).toEqual([
+ expectedClickBreadcrumb,
+ expectedNavigationBreadcrumb,
+ expectedClickBreadcrumb,
+ ]);
+
+ // // -----------------------------------------------------------------------------------------
+ // // And just to finish this off, let's go back to the index page
+
+ await page.click('a');
+
+ const reqPromise8 = waitForReplayRequest(page, 8);
+ const reqPromise9 = waitForReplayRequest(page, 9);
+
+ const replayEvent8 = getReplayEvent(await reqPromise8);
+ const recording8 = getReplayRecordingContent(await reqPromise8);
+
+ expect(replayEvent8).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 8,
+ replay_start_timestamp: undefined,
+ }),
+ );
+ expect(normalize(recording8.fullSnapshots)).toMatchSnapshot('seg-8-snap-full');
+ expect(recording8.incrementalSnapshots.length).toEqual(0);
+
+ await page.click('#go-background');
+
+ const replayEvent9 = getReplayEvent(await reqPromise9);
+ const recording9 = getReplayRecordingContent(await reqPromise9);
+
+ expect(replayEvent9).toEqual(
+ getExpectedReplayEvent({
+ segment_id: 9,
+ urls: [],
+ replay_start_timestamp: undefined,
+ }),
+ );
+ expect(recording9.fullSnapshots.length).toEqual(0);
+ expect(normalize(recording9.incrementalSnapshots)).toMatchSnapshot('seg-9-snap-incremental');
+
+ const collectedPerformanceSpansAfterIndexNavigation = [
+ ...recording8.performanceSpans,
+ ...recording9.performanceSpans,
+ ];
+ const collectedBreadcrumbsAfterIndexNavigation = [...recording8.breadcrumbs, ...recording9.breadcrumbs];
+
+ expect(collectedPerformanceSpansAfterIndexNavigation.length).toEqual(6);
+ expect(collectedPerformanceSpansAfterIndexNavigation).toEqual(
+ expect.arrayContaining([
+ expectedNavigationPerformanceSpan,
+ expectedLCPPerformanceSpan,
+ expectedFPPerformanceSpan,
+ expectedFCPPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ expectedMemoryPerformanceSpan,
+ ]),
+ );
+
+ expect(collectedBreadcrumbsAfterIndexNavigation).toEqual([expectedClickBreadcrumb]);
+ },
+);
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full
new file mode 100644
index 000000000000..a6c4b9f7c908
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full
@@ -0,0 +1,152 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "h1",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "********* ****",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "spa-navigation"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "***** * *** **********",
+ "id": 16
+ }
+ ],
+ "id": 15
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 17
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 18
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/index.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** **** ** ***** ****",
+ "id": 20
+ }
+ ],
+ "id": 19
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 21
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 22
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium
new file mode 100644
index 000000000000..a6c4b9f7c908
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium
@@ -0,0 +1,152 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "h1",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "********* ****",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "spa-navigation"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "***** * *** **********",
+ "id": 16
+ }
+ ],
+ "id": 15
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 17
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 18
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/index.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** **** ** ***** ****",
+ "id": 20
+ }
+ ],
+ "id": 19
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 21
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 22
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental
new file mode 100644
index 000000000000..d8651b3c919b
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 90,
+ "id": 12,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium
new file mode 100644
index 000000000000..d8651b3c919b
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 90,
+ "id": 12,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental
new file mode 100644
index 000000000000..065aeaf45bd9
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental
@@ -0,0 +1,44 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 157,
+ "y": 90,
+ "id": 15,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 6,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 15
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium
new file mode 100644
index 000000000000..065aeaf45bd9
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium
@@ -0,0 +1,44 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 157,
+ "y": 90,
+ "id": 15,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 6,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 15
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 15,
+ "x": 157,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental
new file mode 100644
index 000000000000..9ca283b6ba8f
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental
@@ -0,0 +1,44 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 90,
+ "id": 12,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 6,
+ "id": 15
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium
new file mode 100644
index 000000000000..9ca283b6ba8f
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium
@@ -0,0 +1,44 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 90,
+ "id": 12,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 6,
+ "id": 15
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 12
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 12,
+ "x": 41,
+ "y": 90
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium
new file mode 100644
index 000000000000..cfd7928e8d9d
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium
@@ -0,0 +1,109 @@
+[
+ {
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "id": "go-background"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "*** ***",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "a",
+ "attributes": {
+ "href": "/page-0.html"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "** ** *** ****",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 15
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium
new file mode 100644
index 000000000000..fd46cda96828
--- /dev/null
+++ b/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium
@@ -0,0 +1,39 @@
+[
+ {
+ "source": 1,
+ "positions": [
+ {
+ "x": 41,
+ "y": 18,
+ "id": 9,
+ "timeOffset": 0
+ }
+ ]
+ },
+ {
+ "source": 2,
+ "type": 1,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 5,
+ "id": 9
+ },
+ {
+ "source": 2,
+ "type": 0,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ },
+ {
+ "source": 2,
+ "type": 2,
+ "id": 9,
+ "x": 41,
+ "y": 18
+ }
+]
\ No newline at end of file
diff --git a/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy.json b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy.json
new file mode 100644
index 000000000000..d0cb968d61ff
--- /dev/null
+++ b/packages/integration-tests/suites/replay/privacy/test.ts-snapshots/privacy.json
@@ -0,0 +1,266 @@
+{
+ "node": {
+ "type": 0,
+ "childNodes": [
+ {
+ "type": 1,
+ "name": "html",
+ "publicId": "",
+ "systemId": "",
+ "id": 2
+ },
+ {
+ "type": 2,
+ "tagName": "html",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "head",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "meta",
+ "attributes": {
+ "charset": "utf-8"
+ },
+ "childNodes": [],
+ "id": 5
+ }
+ ],
+ "id": 4
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 6
+ },
+ {
+ "type": 2,
+ "tagName": "body",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 8
+ },
+ {
+ "type": 2,
+ "tagName": "button",
+ "attributes": {
+ "aria-label": "***** **",
+ "onclick": "console.log('Test log')"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "***** **",
+ "id": 10
+ }
+ ],
+ "id": 9
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 11
+ },
+ {
+ "type": 2,
+ "tagName": "div",
+ "attributes": {},
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "**** ****** ** ****** ** *******",
+ "id": 13
+ }
+ ],
+ "id": 12
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 14
+ },
+ {
+ "type": 2,
+ "tagName": "div",
+ "attributes": {
+ "data-sentry-unmask": ""
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "This should be unmasked due to data attribute",
+ "id": 16
+ }
+ ],
+ "id": 15
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 17
+ },
+ {
+ "type": 2,
+ "tagName": "input",
+ "attributes": {
+ "placeholder": "*********** ****** ** ******"
+ },
+ "childNodes": [],
+ "id": 18
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 19
+ },
+ {
+ "type": 2,
+ "tagName": "div",
+ "attributes": {
+ "title": "***** ****** ** ******"
+ },
+ "childNodes": [
+ {
+ "type": 3,
+ "textContent": "***** ****** ** ******",
+ "id": 21
+ }
+ ],
+ "id": 20
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 22
+ },
+ {
+ "type": 2,
+ "tagName": "svg",
+ "attributes": {
+ "rr_width": "200px",
+ "rr_height": "200px"
+ },
+ "childNodes": [],
+ "isSVG": true,
+ "id": 23
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 24
+ },
+ {
+ "type": 2,
+ "tagName": "svg",
+ "attributes": {
+ "style": "width:200px;height:200px",
+ "viewBox": "0 0 80 80",
+ "data-sentry-unblock": ""
+ },
+ "childNodes": [
+ {
+ "type": 2,
+ "tagName": "path",
+ "attributes": {
+ "d": ""
+ },
+ "childNodes": [],
+ "isSVG": true,
+ "id": 26
+ },
+ {
+ "type": 2,
+ "tagName": "area",
+ "attributes": {},
+ "childNodes": [],
+ "isSVG": true,
+ "id": 27
+ },
+ {
+ "type": 2,
+ "tagName": "rect",
+ "attributes": {},
+ "childNodes": [],
+ "isSVG": true,
+ "id": 28
+ }
+ ],
+ "isSVG": true,
+ "id": 25
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 29
+ },
+ {
+ "type": 2,
+ "tagName": "img",
+ "attributes": {
+ "rr_width": "100px",
+ "rr_height": "100px"
+ },
+ "childNodes": [],
+ "id": 30
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 31
+ },
+ {
+ "type": 2,
+ "tagName": "img",
+ "attributes": {
+ "data-sentry-unblock": "",
+ "style": "width:100px;height:100px",
+ "src": "file:///none.png"
+ },
+ "childNodes": [],
+ "id": 32
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 33
+ },
+ {
+ "type": 2,
+ "tagName": "video",
+ "attributes": {
+ "rr_width": "30px",
+ "rr_height": "30px"
+ },
+ "childNodes": [],
+ "id": 34
+ },
+ {
+ "type": 3,
+ "textContent": "\n ",
+ "id": 35
+ },
+ {
+ "type": 3,
+ "textContent": "\n\n",
+ "id": 36
+ }
+ ],
+ "id": 7
+ }
+ ],
+ "id": 3
+ }
+ ],
+ "id": 1
+ },
+ "initialOffset": {
+ "left": 0,
+ "top": 0
+ }
+}
\ No newline at end of file
diff --git a/packages/integration-tests/utils/fixtures.ts b/packages/integration-tests/utils/fixtures.ts
index 15c41ab10c14..0d4c75353700 100644
--- a/packages/integration-tests/utils/fixtures.ts
+++ b/packages/integration-tests/utils/fixtures.ts
@@ -57,6 +57,20 @@ const sentryTest = base.extend({
await generatePage(init, subject, template, testDir);
}
+
+ const additionalPages = fs
+ .readdirSync(testDir)
+ .filter(filename => filename.startsWith('page-') && filename.endsWith('.html'));
+
+ const outDir = path.dirname(testInfo.file);
+ for (const pageFilename of additionalPages) {
+ // create a new page with the same subject and init as before
+ const subject = getAsset(testDir, 'subject.js');
+ const pageFile = getAsset(testDir, pageFilename);
+ const init = getAsset(testDir, 'init.js');
+ await generatePage(init, subject, pageFile, outDir, pageFilename);
+ }
+
return pagePath;
});
},
diff --git a/packages/integration-tests/utils/generatePage.ts b/packages/integration-tests/utils/generatePage.ts
index fea623129e93..e48485101dd3 100644
--- a/packages/integration-tests/utils/generatePage.ts
+++ b/packages/integration-tests/utils/generatePage.ts
@@ -10,9 +10,10 @@ export async function generatePage(
subjectPath: string,
templatePath: string,
outPath: string,
+ outPageName: string = 'index.html',
): Promise {
const localPath = `${outPath}/dist`;
- const bundlePath = `${localPath}/index.html`;
+ const bundlePath = `${localPath}/${outPageName}}`;
if (!existsSync(localPath)) {
mkdirSync(localPath, { recursive: true });
@@ -33,7 +34,7 @@ export async function generatePage(
plugins: [
new SentryScenarioGenerationPlugin(),
new HtmlWebpackPlugin({
- filename: 'index.html',
+ filename: outPageName,
template: templatePath,
inject: 'body',
}),
diff --git a/packages/integration-tests/utils/replayEventTemplates.ts b/packages/integration-tests/utils/replayEventTemplates.ts
index 17aa903a0391..3a4b4b5f9789 100644
--- a/packages/integration-tests/utils/replayEventTemplates.ts
+++ b/packages/integration-tests/utils/replayEventTemplates.ts
@@ -71,6 +71,25 @@ export const expectedNavigationPerformanceSpan = {
},
};
+export const expectedNavigationPushPerformanceSpan = {
+ op: 'navigation.push',
+ description: expect.any(String),
+ startTimestamp: expect.any(Number),
+ endTimestamp: expect.any(Number),
+ data: {},
+};
+
+export const expectedReloadPerformanceSpan = {
+ op: 'navigation.reload',
+ description: '',
+ startTimestamp: expect.any(Number),
+ endTimestamp: expect.any(Number),
+ data: {
+ duration: expect.any(Number),
+ size: expect.any(Number),
+ },
+};
+
export const expectedMemoryPerformanceSpan = {
op: 'memory',
description: 'memory',
@@ -122,3 +141,13 @@ export const expectedClickBreadcrumb = {
nodeId: expect.any(Number),
},
};
+
+export const expectedNavigationBreadcrumb = {
+ timestamp: expect.any(Number),
+ type: 'default',
+ category: 'navigation',
+ data: {
+ from: expect.any(String),
+ to: expect.any(String),
+ },
+};
diff --git a/packages/integration-tests/utils/replayHelpers.ts b/packages/integration-tests/utils/replayHelpers.ts
index 11a4131e6753..703cb11e0393 100644
--- a/packages/integration-tests/utils/replayHelpers.ts
+++ b/packages/integration-tests/utils/replayHelpers.ts
@@ -86,6 +86,16 @@ export function getReplayEvent(replayRequest: Request): ReplayEvent {
return event;
}
+type CustomRecordingContent = {
+ breadcrumbs: Breadcrumb[];
+ performanceSpans: PerformanceSpan[];
+};
+
+type RecordingContent = {
+ fullSnapshots: RecordingSnapshot[];
+ incrementalSnapshots: RecordingSnapshot[];
+} & CustomRecordingContent;
+
/**
* Takes an uncompressed replay request and returns the custom recording events,
* i.e. the events we emit as type 5 rrweb events
@@ -93,10 +103,7 @@ export function getReplayEvent(replayRequest: Request): ReplayEvent {
* @param replayRequest
* @returns an object containing the replay breadcrumbs and performance spans
*/
-export function getCustomRecordingEvents(replayRequest: Request): {
- breadcrumbs: Breadcrumb[];
- performanceSpans: PerformanceSpan[];
-} {
+export function getCustomRecordingEvents(replayRequest: Request): CustomRecordingContent {
const recordingEvents = getDecompressedRecordingEvents(replayRequest);
const breadcrumbs = getReplayBreadcrumbs(recordingEvents);
@@ -126,10 +133,23 @@ export function getFullRecordingSnapshots(replayRequest: Request): RecordingSnap
return events.filter(event => event.type === 2).map(event => event.data as RecordingSnapshot);
}
+function getincrementalRecordingSnapshots(replayRequest: Request): RecordingSnapshot[] {
+ const events = getDecompressedRecordingEvents(replayRequest) as RecordingEvent[];
+ return events.filter(event => event.type === 3).map(event => event.data as RecordingSnapshot);
+}
+
function getDecompressedRecordingEvents(replayRequest: Request): RecordingEvent[] {
return replayEnvelopeRequestParser(replayRequest, 5) as RecordingEvent[];
}
+export function getReplayRecordingContent(replayRequest: Request): RecordingContent {
+ const fullSnapshots = getFullRecordingSnapshots(replayRequest);
+ const incrementalSnapshots = getincrementalRecordingSnapshots(replayRequest);
+ const customEvents = getCustomRecordingEvents(replayRequest);
+
+ return { fullSnapshots, incrementalSnapshots, ...customEvents };
+}
+
/**
* Copy of the envelopeParser from ./helpers.ts, but with the ability
* to decompress zlib-compressed envelope payloads which we need to inspect for replay recordings.
@@ -189,3 +209,14 @@ export function shouldSkipReplayTest(): boolean {
const bundle = process.env.PW_BUNDLE as string | undefined;
return bundle != null && !bundle.includes('replay') && !bundle.includes('esm') && !bundle.includes('cjs');
}
+
+/**
+ * Takes a replay recording payload and returns a normalized string representation.
+ * This is necessary because the DOM snapshots contain absolute paths to other HTML
+ * files which break the tests on different machines.
+ */
+export function normalize(obj: unknown): string {
+ const rawString = JSON.stringify(obj, null, 2);
+ const normalizedString = rawString.replace(/"file:\/\/.+(\/.*\.html)"/g, '"$1"');
+ return normalizedString;
+}