import { markRaw } from '@vue/reactivity' ; export const enum NodeTypes { TEXT = 'text' , ELEMENT = 'element' , COMMENT = 'comment' } export const enum NodeOpTypes { CREATE = 'create' , INSERT = 'insert' , REMOVE = 'remove' , SET_TEXT = 'setText' , SET_ELEMENT_TEXT = 'setElementText' , PATCH = 'patch' } export interface TestElement { id : number ; type : NodeTypes . ELEMENT ; parentNode : TestElement | null ; tag : string ; children : TestNode [ ] ; props : Record < string , any > ; eventListeners : Record < string , Function | Function [ ] > | null ; } export interface TestText { id : number ; type : NodeTypes . TEXT ; parentNode : TestElement | null ; text : string ; } export interface TestComment { id : number ; type : NodeTypes . COMMENT ; parentNode : TestElement | null ; text : string ; } export type TestNode = TestElement | TestText | TestComment ; export interface NodeOp { type : NodeOpTypes ; nodeType ? : NodeTypes ; tag ? : string ; text ? : string ; targetNode ? : TestNode ; parentNode ? : TestElement ; refNode ? : TestNode | null ; propKey ? : string ; propPrevValue ? : any ; propNextValue ? : any ; } let nodeId = 0 ; let recordedNodeOps = [ ] ; export function logNodeOp ( op ) { recordedNodeOps . push ( op ) ; } export function resetOps ( ) { recordedNodeOps = [ ] ; } export function dumpOps ( ) { const ops = recordedNodeOps . slice ( ) ; resetOps ( ) ; return ops ; } function createElement ( tag ) { const node = { id : nodeId ++ , type : NodeTypes . ELEMENT , tag , children : [ ] , props : { } , parentNode : null , eventListeners : null } ; logNodeOp ( { type : NodeOpTypes . CREATE , nodeType : NodeTypes . ELEMENT , targetNode : node , tag } ) ; markRaw ( node ) ; return node ; } function createText ( text ) { const node = { id : nodeId ++ , type : NodeTypes . TEXT , text , parentNode : null } ; logNodeOp ( { type : NodeOpTypes . CREATE , nodeType : NodeTypes . TEXT , targetNode : node , text } ) ; markRaw ( node ) ; return node ; } function createComment ( text ) { const node = { id : nodeId ++ , type : NodeTypes . COMMENT , text , parentNode : null } ; logNodeOp ( { type : NodeOpTypes . CREATE , nodeType : NodeTypes . COMMENT , targetNode : node , text } ) ; markRaw ( node ) ; return node ; } function setText ( node , text ) { logNodeOp ( { type : NodeOpTypes . SET_TEXT , targetNode : node , text } ) ; node . text = text ; } function insert ( child , parent , ref ? ) { let refIndex ; if ( ref ) { refIndex = parent . children . indexOf ( ref ) ; if ( refIndex === - 1 ) { console . error ( 'ref: ' , ref ) ; console . error ( 'parent: ' , parent ) ; throw new Error ( 'ref is not a child of parent' ) ; } } logNodeOp ( { type : NodeOpTypes . INSERT , targetNode : child , parentNode : parent , refNode : ref } ) ; remove ( child , false ) ; refIndex = ref ? parent . children . indexOf ( ref ) : - 1 ; if ( refIndex === - 1 ) { parent . children . push ( child ) ; child . parentNode = parent ; } else { parent . children . splice ( refIndex , 0 , child ) ; child . parentNode = parent ; } } function remove ( child , logOp = true ) { const parent = child . parentNode ; if ( parent ) { if ( logOp ) { logNodeOp ( { type : NodeOpTypes . REMOVE , targetNode : child , parentNode : parent } ) ; } const i = parent . children . indexOf ( child ) ; if ( i > - 1 ) { parent . children . splice ( i , 1 ) ; } else { console . error ( 'target: ' , child ) ; console . error ( 'parent: ' , parent ) ; throw Error ( 'target is not a childNode of parent' ) ; } child . parentNode = null ; } } function setElementText ( el , text ) { logNodeOp ( { type : NodeOpTypes . SET_ELEMENT_TEXT , targetNode : el , text } ) ; el . children . forEach ( c => { c . parentNode = null ; } ) ; if ( ! text ) { el . children = [ ] ; } else { el . children = [ { id : nodeId ++ , type : NodeTypes . TEXT , text , parentNode : el } ] ; } } function parentNode ( node ) { return node . parentNode ; } function nextSibling ( node ) { const parent = node . parentNode ; if ( ! parent ) { return null ; } const i = parent . children . indexOf ( node ) ; return parent . children [ i + 1 ] || null ; } function querySelector ( ) { throw new Error ( 'querySelector not supported in test renderer.' ) ; } function setScopeId ( el , id ) { el . props [ id ] = '' ; } export const nodeOps = { insert , remove , createElement , createText , createComment , setText , setElementText , parentNode , nextSibling , querySelector , setScopeId } ;