child nodes without __sn now remove without error (#307)

This commit is contained in:
Justin Halsall
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 58504e7d77
commit 95dd144227
3 changed files with 121 additions and 7 deletions

View File

@@ -307,9 +307,11 @@ export class TreeIndex {
const deepRemoveFromMirror = (id: number) => {
this.removeIdSet.add(id);
const node = mirror.getNode(id);
node?.childNodes.forEach((childNode) =>
deepRemoveFromMirror(((childNode as unknown) as INode).__sn.id),
);
node?.childNodes.forEach((childNode) => {
if ('__sn' in childNode) {
deepRemoveFromMirror(((childNode as unknown) as INode).__sn.id)
}
});
};
const deepRemoveFromTreeIndex = (node: TreeNode) => {
this.removeIdSet.add(node.id);

View File

@@ -5,8 +5,11 @@ import * as path from 'path';
import * as puppeteer from 'puppeteer';
import { expect } from 'chai';
import { Suite } from 'mocha';
import { launchPuppeteer, sampleEvents as events } from './utils';
import { EventType } from '../src/types';
import {
launchPuppeteer,
sampleEvents as events,
sampleStyleSheetRemoveEvents as stylesheetRemoveEvents
} from './utils';
interface ISuite extends Suite {
code: string;
@@ -28,7 +31,7 @@ describe('replayer', function (this: ISuite) {
const page: puppeteer.Page = await this.browser.newPage();
await page.goto('about:blank');
await page.evaluate(this.code);
await page.evaluate(`const events = ${JSON.stringify(events)}`);
await page.evaluate(`let events = ${JSON.stringify(events)}`);
this.page = page;
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
@@ -132,6 +135,19 @@ describe('replayer', function (this: ISuite) {
expect(currentState).to.equal('paused');
});
it('can handle removing style elements', async () => {
await this.page.evaluate(`events = ${JSON.stringify(stylesheetRemoveEvents)}`);
const actionLength = await this.page.evaluate(`
const { Replayer } = rrweb;
const replayer = new Replayer(events);
replayer.play(2500);
replayer['timer']['actions'].length;
`);
expect(actionLength).to.equal(
stylesheetRemoveEvents.filter((e) => e.timestamp - stylesheetRemoveEvents[0].timestamp >= 2500).length,
);
});
it('can stream events in live mode', async () => {
const status = await this.page.evaluate(`
const { Replayer } = rrweb;
@@ -142,5 +158,5 @@ describe('replayer', function (this: ISuite) {
replayer.service.state.value;
`);
expect(status).to.equal('live');
})
});
});

View File

@@ -202,3 +202,99 @@ export const sampleEvents: eventWithTime[] = [
timestamp: now + 4000,
},
];
export const sampleStyleSheetRemoveEvents: eventWithTime[] = [
{
type: EventType.DomContentLoaded,
data: {},
timestamp: now,
},
{
type: EventType.Load,
data: {},
timestamp: now + 1000,
},
{
type: EventType.Meta,
data: {
href: 'http://localhost',
width: 1000,
height: 800,
},
timestamp: now + 1000,
},
{
type: EventType.FullSnapshot,
data: {
node: {
type: 0,
childNodes: [
{
type: 2,
tagName: 'html',
attributes: {},
childNodes: [
{
type: 2,
tagName: 'head',
attributes: {},
childNodes: [
{
type: 2,
tagName: "style",
attributes: {
"data-jss": "",
"data-meta": "OverlayDrawer",
_cssText: ".OverlayDrawer-modal-187 { }.OverlayDrawer-paper-188 { width: 100%; }@media (min-width: 48em) {\n .OverlayDrawer-paper-188 { width: 38rem; }\n}@media (min-width: 48em) {\n}@media (min-width: 48em) {\n}"
},
childNodes: [
{
type: 3,
textContent: "\n",
isStyle: true,
id: 5
},
],
id: 4
},
],
id: 3,
},
{
type: 2,
tagName: 'body',
attributes: {},
childNodes: [],
id: 6,
},
],
id: 2,
},
],
id: 1,
},
initialOffset: {
top: 0,
left: 0,
},
},
timestamp: now + 1000,
},
{
type: EventType.IncrementalSnapshot,
data: {
source: IncrementalSource.Mutation,
texts: [],
attributes: [],
removes: [
{
parentId: 3,
id: 4
}
],
adds: []
},
timestamp: now + 2000,
},
];