@@ -175,9 +175,7 @@ describe('Unit | util | handleRecordingEmit', () => {
175175 } ,
176176 } ;
177177
178- vi . spyOn ( record . mirror , 'getNode' ) . mockReturnValue ( target ) ;
179- vi . spyOn ( record . mirror , 'getMeta' ) . mockReturnValue ( meta as serializedElementNodeWithId ) ;
180- vi . spyOn ( record . mirror , 'getId' ) . mockReturnValue ( 42 ) ;
178+ record . mirror . add ( target , meta as serializedElementNodeWithId ) ;
181179
182180 syncMirrorAttributesFromMutationEvent ( {
183181 type : EventType . IncrementalSnapshot ,
@@ -237,8 +235,7 @@ describe('Unit | util | handleRecordingEmit', () => {
237235 } ,
238236 } ;
239237
240- vi . spyOn ( record . mirror , 'getNode' ) . mockReturnValue ( target ) ;
241- vi . spyOn ( record . mirror , 'getMeta' ) . mockReturnValue ( meta as serializedElementNodeWithId ) ;
238+ record . mirror . add ( target , meta as serializedElementNodeWithId ) ;
242239
243240 syncMirrorAttributesFromMutationEvent ( {
244241 type : EventType . IncrementalSnapshot ,
@@ -259,6 +256,74 @@ describe('Unit | util | handleRecordingEmit', () => {
259256 } ,
260257 } ) ;
261258
262- expect ( meta . attributes [ 'aria-label' ] ) . toBe ( '*********' ) ;
259+ expect ( record . mirror . getMeta ( target ) ?. attributes [ 'aria-label' ] ) . toBe ( '*********' ) ;
260+ } ) ;
261+
262+ it ( 'does not rewrite the serialized node that was already emitted in an `adds` payload' , function ( ) {
263+ const target = document . createElement ( 'div' ) ;
264+
265+ // rrweb stores the very same object in the mirror that it emits in `adds`, so a
266+ // previously emitted event and the mirror share this reference.
267+ const meta = {
268+ id : 42 ,
269+ type : NodeType . Element ,
270+ tagName : 'div' ,
271+ childNodes : [ ] ,
272+ attributes : {
273+ id : 'popover' ,
274+ style : 'position: fixed; left: 0px; top: 0px; transform: translate(0px, -200%); min-width: max-content;' ,
275+ } ,
276+ } ;
277+
278+ record . mirror . add ( target , meta as serializedElementNodeWithId ) ;
279+
280+ const addEvent = {
281+ type : EventType . IncrementalSnapshot ,
282+ timestamp : BASE_TIMESTAMP ,
283+ data : {
284+ source : IncrementalSource . Mutation ,
285+ texts : [ ] ,
286+ attributes : [ ] ,
287+ removes : [ ] ,
288+ adds : [ { parentId : 1 , nextId : null , node : meta } ] ,
289+ } ,
290+ } ;
291+
292+ // rrweb emits a compact style mutation, where `style` is a partial diff object rather
293+ // than the full style string.
294+ syncMirrorAttributesFromMutationEvent ( {
295+ type : EventType . IncrementalSnapshot ,
296+ timestamp : BASE_TIMESTAMP + 10 ,
297+ data : {
298+ source : IncrementalSource . Mutation ,
299+ texts : [ ] ,
300+ attributes : [
301+ {
302+ id : 42 ,
303+ attributes : {
304+ id : 'popover-open' ,
305+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
306+ style : { transform : 'translate(631px, 210px)' } as any ,
307+ } ,
308+ } ,
309+ ] ,
310+ removes : [ ] ,
311+ adds : [ ] ,
312+ } ,
313+ } ) ;
314+
315+ // The already emitted event still describes the element as it was when it was serialized.
316+ // Buffers that hold events unserialized (i.e. when compression is disabled) would otherwise
317+ // ship this partial style diff in place of the full inline style.
318+ expect ( addEvent . data . adds [ 0 ] ?. node . attributes ) . toEqual ( {
319+ id : 'popover' ,
320+ style : 'position: fixed; left: 0px; top: 0px; transform: translate(0px, -200%); min-width: max-content;' ,
321+ } ) ;
322+
323+ // But the mirror is up to date for the attributes that click breadcrumbs care about.
324+ expect ( record . mirror . getMeta ( target ) ?. attributes ) . toEqual ( {
325+ id : 'popover-open' ,
326+ style : 'position: fixed; left: 0px; top: 0px; transform: translate(0px, -200%); min-width: max-content;' ,
327+ } ) ;
263328 } ) ;
264329} ) ;
0 commit comments