@@ -302,7 +302,7 @@ describeWithEnvironment('DOMTreeWidget', () => {
302302 rootTreeElement . expand ( ) ;
303303 domTree . performUpdate ( ) ;
304304
305- await new Promise ( resolve => setTimeout ( resolve , 0 ) ) ;
305+ await UI . Widget . Widget . allUpdatesComplete ;
306306
307307 await assertScreenshot ( 'elements/elements_tree_outline_default.png' ) ;
308308 } finally {
@@ -311,4 +311,249 @@ describeWithEnvironment('DOMTreeWidget', () => {
311311 } ) ;
312312 } ) ;
313313
314+ describe ( 'DECLARATIVE_VIEW' , ( ) => {
315+ it ( 'renders DOM tree declaratively using <devtools-tree> and ElementsTreeWidget' , async ( ) => {
316+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
317+ try {
318+ const rootNode = createTestDOMTree ( domModel , {
319+ nodeId : 1 ,
320+ nodeName : 'DIV' ,
321+ children : [ { nodeId : 2 , nodeName : '#text' , nodeValue : 'Hello world' } ] ,
322+ } ) ;
323+ domTree . rootDOMNode = rootNode ;
324+ domTree . performUpdate ( ) ;
325+
326+ // Ensure ElementsTreeOutline is not created in declarative mode.
327+ assert . isUndefined ( domTree . getTreeOutlineForTesting ( ) ) ;
328+
329+ // Wait for devtools-tree to render its template.
330+ await UI . Widget . Widget . allUpdatesComplete ;
331+
332+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
333+ assert . exists ( tree ) ;
334+
335+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
336+ const rootTreeElements = internalTree . rootElement ( ) . children ( ) ;
337+ assert . lengthOf ( rootTreeElements , 1 ) ;
338+
339+ // Verify ElementsTreeWidget is rendered for the root node.
340+ const widgetElement = rootTreeElements [ 0 ] . listItemElement . querySelector ( 'devtools-widget' ) ;
341+ assert . exists ( widgetElement ) ;
342+ const widget = UI . Widget . Widget . get ( widgetElement ) ;
343+ assert . instanceOf ( widget , Elements . ElementsTreeElement . ElementsTreeWidget ) ;
344+ assert . strictEqual ( ( widget as Elements . ElementsTreeElement . ElementsTreeWidget ) . node , rootNode ) ;
345+ } finally {
346+ domTree . detach ( ) ;
347+ }
348+ } ) ;
349+
350+ it ( 'handles selection and expansion in declarative view' , async ( ) => {
351+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
352+ try {
353+ const rootNode = createTestDOMTree ( domModel , {
354+ nodeId : 1 ,
355+ nodeName : 'DIV' ,
356+ children : [ { nodeId : 2 , nodeName : 'SPAN' } ] ,
357+ } ) ;
358+ domTree . rootDOMNode = rootNode ;
359+
360+ const childNode = rootNode . children ( ) ! [ 0 ] ;
361+ domTree . selectDOMNode ( childNode ) ;
362+ assert . strictEqual ( domTree . selectedDOMNode ( ) , childNode ) ;
363+ assert . isTrue ( domTree . isNodeExpanded ( rootNode ) ) ;
364+
365+ domTree . setNodeExpanded ( rootNode , false ) ;
366+ assert . isFalse ( domTree . isNodeExpanded ( rootNode ) ) ;
367+ } finally {
368+ domTree . detach ( ) ;
369+ }
370+ } ) ;
371+
372+ it ( 'supports omitRootDOMNode in declarative view' , async ( ) => {
373+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
374+ try {
375+ domTree . omitRootDOMNode = true ;
376+ const rootNode = createTestDOMTree ( domModel , {
377+ nodeId : 1 ,
378+ nodeName : 'BODY' ,
379+ children : [ { nodeId : 2 , nodeName : 'H1' } ] ,
380+ } ) ;
381+ domTree . rootDOMNode = rootNode ;
382+ domTree . performUpdate ( ) ;
383+
384+ // Wait for devtools-tree to render its template.
385+ await UI . Widget . Widget . allUpdatesComplete ;
386+
387+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
388+ assert . exists ( tree ) ;
389+
390+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
391+ const children = internalTree . rootElement ( ) . children ( ) ;
392+ assert . lengthOf ( children , 1 ) ;
393+
394+ const widgetElement = children [ 0 ] . listItemElement . querySelector ( 'devtools-widget' ) ;
395+ assert . exists ( widgetElement ) ;
396+ const widget = UI . Widget . Widget . get ( widgetElement ) as Elements . ElementsTreeElement . ElementsTreeWidget ;
397+ assert . exists ( widget ) ;
398+ assert . strictEqual ( widget . node , rootNode . children ( ) ! [ 0 ] ) ;
399+ } finally {
400+ domTree . detach ( ) ;
401+ }
402+ } ) ;
403+
404+ it ( 'fetches children asynchronously when children are not loaded initially' , async ( ) => {
405+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
406+ try {
407+ domTree . omitRootDOMNode = true ;
408+ const rootNode = SDK . DOMModel . DOMNode . create ( domModel , null , false , {
409+ nodeId : 1 as Protocol . DOM . NodeId ,
410+ backendNodeId : 1 as Protocol . DOM . BackendNodeId ,
411+ nodeType : Node . DOCUMENT_NODE ,
412+ nodeName : '#document' ,
413+ localName : '' ,
414+ nodeValue : '' ,
415+ childNodeCount : 1 ,
416+ } ) ;
417+ assert . isNotNull ( rootNode ) ;
418+
419+ let getChildNodesCallback : ( ( children : SDK . DOMModel . DOMNode [ ] | null ) => void ) | undefined ;
420+ sinon . stub ( rootNode , 'getChildNodes' ) . callsFake ( callback => {
421+ getChildNodesCallback = callback ;
422+ const htmlNode = createTestDOMTree ( domModel , { nodeId : 2 , nodeName : 'HTML' } ) ;
423+ rootNode . childrenInternal = [ htmlNode ] ;
424+ } ) ;
425+
426+ domTree . rootDOMNode = rootNode ;
427+ domTree . performUpdate ( ) ;
428+
429+ assert . isDefined ( getChildNodesCallback ) ;
430+ getChildNodesCallback ?.( rootNode . childrenInternal ) ;
431+ domTree . performUpdate ( ) ;
432+
433+ await UI . Widget . Widget . allUpdatesComplete ;
434+
435+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
436+ assert . exists ( tree ) ;
437+
438+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
439+ const children = internalTree . rootElement ( ) . children ( ) ;
440+ assert . lengthOf ( children , 1 ) ;
441+ } finally {
442+ domTree . detach ( ) ;
443+ }
444+ } ) ;
445+
446+ it ( 'renders screenshot of declarative view' , async ( ) => {
447+ const { domTree, domModel} =
448+ setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW , { includeCommonStyles : true } ) ;
449+ try {
450+ const rootNode = createTestDOMTree ( domModel , {
451+ nodeId : 1 ,
452+ nodeName : 'DIV' ,
453+ attributes : [ 'id' , 'container' , 'class' , 'main-view' ] ,
454+ children : [
455+ { nodeId : 2 , nodeName : 'H1' , children : [ { nodeId : 3 , nodeName : '#text' , nodeValue : 'Title' } ] } ,
456+ { nodeId : 4 , nodeName : 'SPAN' , children : [ { nodeId : 5 , nodeName : '#text' , nodeValue : 'Description' } ] } ,
457+ ] ,
458+ } ) ;
459+ domTree . rootDOMNode = rootNode ;
460+ domTree . setNodeExpanded ( rootNode , true ) ;
461+ domTree . performUpdate ( ) ;
462+
463+ await UI . Widget . Widget . allUpdatesComplete ;
464+
465+ await assertScreenshot ( 'elements/elements_tree_outline_declarative.png' ) ;
466+ } finally {
467+ domTree . detach ( ) ;
468+ }
469+ } ) ;
470+
471+ it ( 'supports maxTreeDepth in declarative view' , async ( ) => {
472+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
473+ try {
474+ const rootNode = createTestDOMTree ( domModel , {
475+ nodeId : 1 ,
476+ nodeName : 'DIV' ,
477+ children : [ {
478+ nodeId : 2 ,
479+ nodeName : 'P' ,
480+ children : [ { nodeId : 3 , nodeName : 'SPAN' } ] ,
481+ } ] ,
482+ } ) ;
483+ domTree . rootDOMNode = rootNode ;
484+ domTree . maxTreeDepth = 1 ;
485+ domTree . setNodeExpanded ( rootNode , true ) ;
486+ domTree . performUpdate ( ) ;
487+
488+ await UI . Widget . Widget . allUpdatesComplete ;
489+
490+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
491+ assert . exists ( tree ) ;
492+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
493+ const rootTreeElements = internalTree . rootElement ( ) . children ( ) ;
494+ assert . lengthOf ( rootTreeElements , 1 ) ;
495+ } finally {
496+ domTree . detach ( ) ;
497+ }
498+ } ) ;
499+
500+ it ( 'filters comment nodes based on showComments setting in declarative view' , async ( ) => {
501+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
502+ try {
503+ const rootNode = createTestDOMTree ( domModel , {
504+ nodeId : 1 ,
505+ nodeName : 'DIV' ,
506+ children : [
507+ { nodeId : 2 , nodeName : '#comment' , nodeType : Node . COMMENT_NODE , nodeValue : 'comment text' } ,
508+ { nodeId : 3 , nodeName : 'SPAN' } ,
509+ ] ,
510+ } ) ;
511+ domTree . rootDOMNode = rootNode ;
512+ domTree . showComments = false ;
513+ domTree . setNodeExpanded ( rootNode , true ) ;
514+ domTree . performUpdate ( ) ;
515+
516+ await UI . Widget . Widget . allUpdatesComplete ;
517+
518+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
519+ assert . exists ( tree ) ;
520+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
521+ const rootTreeElements = internalTree . rootElement ( ) . children ( ) ;
522+ assert . isNotEmpty ( rootTreeElements ) ;
523+ // With comments filtered out, child elements are [SPAN, </DIV>].
524+ const childTreeElements = rootTreeElements [ 0 ] . children ( ) ;
525+ assert . lengthOf ( childTreeElements , 2 ) ;
526+ const spanWidgetElement = childTreeElements [ 0 ] . listItemElement . querySelector ( 'devtools-widget' ) ;
527+ assert . exists ( spanWidgetElement ) ;
528+ const widget = UI . Widget . Widget . get ( spanWidgetElement ) as Elements . ElementsTreeElement . ElementsTreeWidget ;
529+ assert . strictEqual ( widget . node ?. nodeName ( ) , 'SPAN' ) ;
530+ } finally {
531+ domTree . detach ( ) ;
532+ }
533+ } ) ;
534+
535+ it ( 'sets correct jslog attributes on treeitems in declarative view' , async ( ) => {
536+ const { domTree, domModel} = setupDOMTreeWidget ( target , Elements . ElementsTreeOutline . DECLARATIVE_VIEW ) ;
537+ try {
538+ const rootNode = createTestDOMTree ( domModel , { nodeId : 1 , nodeName : 'DIV' } ) ;
539+ domTree . rootDOMNode = rootNode ;
540+ domTree . performUpdate ( ) ;
541+
542+ await UI . Widget . Widget . allUpdatesComplete ;
543+
544+ const tree = domTree . contentElement . querySelector < UI . TreeOutline . TreeViewElement > ( 'devtools-tree' ) ;
545+ assert . exists ( tree ) ;
546+ const internalTree = tree . getInternalTreeOutlineForTest ( ) ;
547+ const rootTreeElements = internalTree . rootElement ( ) . children ( ) ;
548+ assert . lengthOf ( rootTreeElements , 1 ) ;
549+
550+ const jslog = rootTreeElements [ 0 ] . listItemElement . getAttribute ( 'jslog' ) ;
551+ assert . isNotNull ( jslog ) ;
552+ assert . include ( jslog , 'TreeItem' ) ;
553+ assert . include ( jslog , 'elementsTreeOutline' ) ;
554+ } finally {
555+ domTree . detach ( ) ;
556+ }
557+ } ) ;
558+ } ) ;
314559} ) ;
0 commit comments