import snapshotDiff from 'snapshot-diff' ; import { render } from '@testing-library/react' ; import { disposables } from '../lib/utils/disposables' ; import { reportChanges } from './report-dom-node-changes' ; function redentSnapshot ( input ) { let minSpaces = Infinity ; let lines = input . split ( '\n' ) ; for ( let line of lines ) { if ( line . trim ( ) === '---' ) continue ; let spacesInLine = ( line . match ( /^[+-](\s+)/g ) || [ ] ) . pop ( ) ! . length - 1 ; minSpaces = Math . min ( minSpaces , spacesInLine ) ; } let replacer = new RegExp ( `^([+-])\\s{${ minSpaces }}(.*)` , 'g' ) ; return input . split ( '\n' ) . map ( ( line ) => line . trim ( ) === '---' ? line : line . replace ( replacer , ( _ , sign , rest ) => `${ sign }  ${ rest }` ) ) . join ( '\n' ) ; } export async function executeTimeline ( element , steps ) { let d = disposables ( ) ; let snapshots = [ ] ; let tools = render ( element ) ; d . add ( reportChanges ( ( ) => document . body . innerHTML , ( ) => { snapshots . push ( { content : tools . asFragment ( ) , recordedAt : process . hrtime . bigint ( ) } ) ; } ) ) ; let timestamps = [ null ] ; await steps . reduce ( async ( chain , step ) => { await chain ; let durations = step ( tools ) ; timestamps . push ( ... durations ) ; let totalDuration = durations . filter ( ( duration ) : duration is number => duration !== null ) . reduce ( ( total , current ) => total + current , 0 ) ; await new Promise ( ( resolve ) => d . nextFrame ( resolve ) ) ; await new Promise ( ( resolve ) => d . setTimeout ( resolve , totalDuration ) ) ; await new Promise ( ( resolve ) => d . nextFrame ( resolve ) ) ; } , Promise . resolve ( ) ) ; if ( snapshots . length <= 0 ) { throw new Error ( 'We could not record any changes' ) ; } let uniqueSnapshots = snapshots . filter ( ( snapshot , i ) => { if ( i === 0 ) return true ; return snapshot . content !== snapshots [ i - 1 ] . content ; } ) . map ( ( snapshot , i , all ) => ( { ... snapshot , relativeToPreviousSnapshot : i === 0 ? 0 : Number ( ( snapshot . recordedAt - all [ i - 1 ] . recordedAt ) / BigInt ( 1e6 ) ) } ) ) ; let diffed = uniqueSnapshots . map ( ( call , i ) => { if ( i === 0 ) return false ; return `Render ${ i }:${ timestamps [ i ] === null ? '' : ` Transition took at least ${ timestamps [ i ] }ms (${ isWithinFrame ( call . relativeToPreviousSnapshot , timestamps [ i ] ! ) ? 'yes' : `no, it took ${ call . relativeToPreviousSnapshot }ms` })` }\n${ redentSnapshot ( snapshotDiff ( uniqueSnapshots [ i - 1 ] . content , call . content , { aAnnotation : '__REMOVE_ME__' , bAnnotation : '__REMOVE_ME__' , contextLines : 0 } ) . replace ( /\n\n@@([^@@]*)@@/g , '' ) . replace ( /@@([^@@]*)@@/g , '---' ) . replace ( /[-+] __REMOVE_ME__\n/g , '' ) . replace ( /Snapshot Diff:\n/g , '' ) ) . split ( '\n' ) . map ( ( line ) => `    ${ line }` ) . join ( '\n' ) }` ; } ) . filter ( Boolean ) . join ( '\n\n' ) ; d . dispose ( ) ; return diffed ; } executeTimeline . fullTransition = ( duration ) => { return [ null , null , duration ] ; } ; let state = { before : Date . now ( ) , fps : 0 , handle : null } ; state . handle = requestAnimationFrame ( function loop ( ) { let now = Date . now ( ) ; state . fps = Math . round ( 1000 / ( now - state . before ) ) ; state . before = now ; state . handle = requestAnimationFrame ( loop ) ; } ) ; afterAll ( ( ) => { if ( state . handle ) cancelAnimationFrame ( state . handle ) ; } ) ; function isWithinFrame ( actual , expected ) { let buffer = state . fps ; let min = expected - buffer ; let max = expected + buffer ; return actual >= min && actual <= max ; }