namespace XFESystem.Browser.Tests;
public sealed class DomTraversalTests
{
[Fact]
public void TextWalkerTracksActualNodesAfterSplittingAndWrapping()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("first|middle|last,true,true", page.ExecuteJavaScript("""
const root=document.createElement('div');root.innerHTML='first<span>middle</span>last';document.body.appendChild(root);
const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,null),values=[];
let node;while(node=walker.nextNode())values.push(node.nodeValue);
const text=root.firstChild,tail=text.splitText(2),link=document.createElement('a');link.textContent=tail.nodeValue;root.replaceChild(link,tail);
[values.join('|'),text.data==='fi',link.textContent==='rst'].join(',')
"""));
Assert.Equal("fi|rst|middle|last", page.ExecuteJavaScript("walker.currentNode=root;let parts=[];while(node=walker.nextNode())parts.push(node.nodeValue);parts.join('|')"));
}
finally { page.Release(); }
}
[Fact]
public void FiltersKeepSkipRejectAndNavigationSemantics()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
string result = page.ExecuteJavaScript("""
const root=document.createElement('div');root.innerHTML='<section id=skip><i id=visible></i></section><section id=reject><b id=hidden></b></section><p id=kept></p>';
globalThis.walkerVisited=[];const filter={acceptNode(node){if(walkerVisited.length<12)walkerVisited.push(node.id);return node.id==='skip'?NodeFilter.FILTER_SKIP:node.id==='reject'?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}};
const walker=document.createTreeWalker(root,NodeFilter.SHOW_ELEMENT,filter);
[walker.nextNode().id,walker.nextNode().id,walker.previousNode().id==='visible',walker instanceof TreeWalker].join(',')
""");
Assert.True(result == "visible,kept,true,true", result + " | " + page.ExecuteJavaScript("walkerVisited.join('>')"));
}
finally { page.Release(); }
}
[Fact]
public void ReentrantFilterAndInvalidTextOffsetAreCatchableWithoutPartialMutation()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("InvalidStateError,IndexSizeError,abc", page.ExecuteJavaScript("""
const root=document.createElement('div');root.textContent='abc';let first='',second='';
const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,()=>walker.nextNode());
try{walker.nextNode()}catch(e){first=e.name}try{root.firstChild.splitText(99)}catch(e){second=e.name}
[first,second,root.textContent].join(',')
"""));
}
finally { page.Release(); }
}
}
namespace XFESystem.Browser.Tests;
public sealed class DomTraversalTests
{
[Fact]
public void TextWalkerTracksActualNodesAfterSplittingAndWrapping()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("first|middle|last,true,true", page.ExecuteJavaScript("""
const root=document.createElement('div');root.innerHTML='first<span>middle</span>last';document.body.appendChild(root);
const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,null),values=[];
let node;while(node=walker.nextNode())values.push(node.nodeValue);
const text=root.firstChild,tail=text.splitText(2),link=document.createElement('a');link.textContent=tail.nodeValue;root.replaceChild(link,tail);
[values.join('|'),text.data==='fi',link.textContent==='rst'].join(',')
"""));
Assert.Equal("fi|rst|middle|last", page.ExecuteJavaScript("walker.currentNode=root;let parts=[];while(node=walker.nextNode())parts.push(node.nodeValue);parts.join('|')"));
}
finally { page.Release(); }
}
[Fact]
public void FiltersKeepSkipRejectAndNavigationSemantics()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
string result = page.ExecuteJavaScript("""
const root=document.createElement('div');root.innerHTML='<section id=skip><i id=visible></i></section><section id=reject><b id=hidden></b></section><p id=kept></p>';
globalThis.walkerVisited=[];const filter={acceptNode(node){if(walkerVisited.length<12)walkerVisited.push(node.id);return node.id==='skip'?NodeFilter.FILTER_SKIP:node.id==='reject'?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}};
const walker=document.createTreeWalker(root,NodeFilter.SHOW_ELEMENT,filter);
[walker.nextNode().id,walker.nextNode().id,walker.previousNode().id==='visible',walker instanceof TreeWalker].join(',')
""");
Assert.True(result == "visible,kept,true,true", result + " | " + page.ExecuteJavaScript("walkerVisited.join('>')"));
}
finally { page.Release(); }
}
[Fact]
public void ReentrantFilterAndInvalidTextOffsetAreCatchableWithoutPartialMutation()
{
var page = new BrowserService();
try
{
page.Navigate("about:Start");
Assert.Equal("InvalidStateError,IndexSizeError,abc", page.ExecuteJavaScript("""
const root=document.createElement('div');root.textContent='abc';let first='',second='';
const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,()=>walker.nextNode());
try{walker.nextNode()}catch(e){first=e.name}try{root.firstChild.splitText(99)}catch(e){second=e.name}
[first,second,root.textContent].join(',')
"""));
}
finally { page.Release(); }
}
}