// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assert } from 'chai';
import sinon from 'sinon';
import { dispatchKeyDownEvent, renderElementIntoDOM } from '../../testing/DOMHelpers.js';
import * as Lit from '../../ui/lit/lit.js';
import * as UI from './legacy.js';
describe('TreeOutline', () => {
    describe('correctly reacts to Enter key', () => {
        it('by expanding collapsed parent nodes', () => {
            const tree = new UI.TreeOutline.TreeOutlineInShadow();
            renderElementIntoDOM(tree.element);
            const parent = new UI.TreeOutline.TreeElement('parent', true);
            parent.appendChild(new UI.TreeOutline.TreeElement('child', false));
            tree.appendChild(parent);
            parent.select();
            dispatchKeyDownEvent(parent.listItemNode, { bubbles: true, key: 'Enter' });
            assert.isTrue(parent.expanded, 'Enter key was supposed to expand the parent node');
        });
        it('by collapsing expanded parent nodes', () => {
            const tree = new UI.TreeOutline.TreeOutlineInShadow();
            renderElementIntoDOM(tree.element);
            const parent = new UI.TreeOutline.TreeElement('parent', true);
            parent.appendChild(new UI.TreeOutline.TreeElement('child', false));
            tree.appendChild(parent);
            parent.select();
            parent.expand();
            dispatchKeyDownEvent(parent.listItemNode, { bubbles: true, key: 'Enter' });
            assert.isFalse(parent.expanded, 'Enter key was supposed to collapse the parent node');
        });
    });
    it('responds correctly to navigation keys', () => {
        const treeOutline = new UI.TreeOutline.TreeOutlineInShadow();
        renderElementIntoDOM(treeOutline.element);
        for (let i = 0; i < 10; i++) {
            const treeElement = new UI.TreeOutline.TreeElement(String(i), true);
            treeElement.appendChild(new UI.TreeOutline.TreeElement(String(i) + ' child'));
            treeOutline.appendChild(treeElement);
        }
        let selectedTitles = [];
        treeOutline.addEventListener(UI.TreeOutline.Events.ElementSelected, event => {
            selectedTitles.push(String(event.data.title));
        });
        treeOutline.firstChild().select(false, true);
        const distance = 25;
        assert.deepEqual(selectedTitles, ['0']);
        selectedTitles = [];
        function sendKeyMultipleTimes(key, n) {
            for (let i = 0; i < n; i++) {
                sendKey(key);
            }
        }
        sendKeyMultipleTimes('ArrowDown', distance);
        assert.deepEqual(selectedTitles, ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
        selectedTitles = [];
        sendKeyMultipleTimes('ArrowUp', distance);
        assert.deepEqual(selectedTitles, ['8', '7', '6', '5', '4', '3', '2', '1', '0']);
        selectedTitles = [];
        sendKey('End');
        assert.deepEqual(selectedTitles, ['9']);
        selectedTitles = [];
        sendKey('Home');
        assert.deepEqual(selectedTitles, ['0']);
        selectedTitles = [];
        sendKeyMultipleTimes('ArrowRight', distance);
        assert.deepEqual(selectedTitles, ['0 child']);
        selectedTitles = [];
        sendKeyMultipleTimes('ArrowLeft', distance);
        assert.deepEqual(selectedTitles, ['0']);
        selectedTitles = [];
        const innerTreeOutline = new UI.TreeOutline.TreeOutlineInShadow();
        const firstInnerChild = new UI.TreeOutline.TreeElement('First inner child');
        const firstInnerGrandChild = new UI.TreeOutline.TreeElement('First inner grandchild');
        innerTreeOutline.appendChild(firstInnerChild);
        firstInnerChild.appendChild(firstInnerGrandChild);
        const treeElementForNestedTree = new UI.TreeOutline.TreeElement(innerTreeOutline.element);
        treeOutline.appendChild(treeElementForNestedTree);
        innerTreeOutline.firstChild().select(false, true);
        sendKey('ArrowRight');
        assert.isTrue(innerTreeOutline.firstChild().expanded, 'child is not expanded');
        function sendKey(key) {
            const deepActiveElement = UI.DOMUtilities.deepActiveElement(document);
            deepActiveElement.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key }));
        }
    });
});
describe('TreeViewElement', () => {
    const { html, render } = Lit;
    async function makeTree(template) {
        const container = document.createElement('div');
        renderElementIntoDOM(container);
        render(template, container);
        const component = container.querySelector('devtools-tree');
        assert.isNotNull(component);
        // Tick to let mutation observers fire
        await new Promise(resolve => setTimeout(resolve, 0));
        return component;
    }
    it('renders the tree structure based on the template', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
         <li role="treeitem">
           Tree Node Text
           <ul role="group">
             <li role="treeitem">
               Node with subtree
               <ul role="group">
                 <li role="treeitem">Tree Node Text in collapsed subtree 1</li>
                 <li role="treeitem">Tree Node Text in collapsed subtree 2</li>
               </ul>
            </li>
            <li role="treeitem">Tree Node Text in a selected-by-default node</li>
          </ul>
        </li>
      </ul>
    `}></devtools-tree>`);
        const treeOutline = component.getInternalTreeOutlineForTest();
        const rootChildren = treeOutline.rootElement().children();
        assert.lengthOf(rootChildren, 1);
        assert.strictEqual(rootChildren[0].titleElement.textContent?.trim(), 'Tree Node Text');
        const firstLevel = rootChildren[0].children();
        assert.lengthOf(firstLevel, 2);
        assert.strictEqual(firstLevel[0].titleElement.textContent?.trim(), 'Node with subtree');
        assert.strictEqual(firstLevel[1].titleElement.textContent?.trim(), 'Tree Node Text in a selected-by-default node');
        const secondLevel = firstLevel[0].children();
        assert.lengthOf(secondLevel, 2);
        assert.strictEqual(secondLevel[0].titleElement.textContent?.trim(), 'Tree Node Text in collapsed subtree 1');
        assert.strictEqual(secondLevel[1].titleElement.textContent?.trim(), 'Tree Node Text in collapsed subtree 2');
    });
    it('selects `selected` config elements', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <li role="treeitem" selected>second node</li>
      </ul>`}></devtools-tree>`);
        const nodes = component.getInternalTreeOutlineForTest().rootElement().children();
        assert.lengthOf(nodes, 2);
        assert.isFalse(nodes[0].selected);
        assert.isTrue(nodes[1].selected);
    });
    it('expands subtrees based on the `open` attribute', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first subtree
          <ul role="group">
            <li role="treeitem">in first subtree</li>
          </ul>
        </li>
        <li role="treeitem" open>second subtree
          <ul role="group">
            <li role="treeitem">in second subtree</li>
          </ul>
        </li>
      </ul>`}></devtools-tree>`);
        const nodes = component.getInternalTreeOutlineForTest().rootElement().children();
        assert.lengthOf(nodes, 2);
        assert.isFalse(nodes[0].expanded);
        assert.isTrue(nodes[1].expanded);
    });
    it('keeps the node expanded after re-rendering initially expanded', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem" open>subtree
          <ul role="group">
            <li role="treeitem">in subtree</li>
          </ul>
        </li>
      </ul>`}></devtools-tree>`);
        const node = component.getInternalTreeOutlineForTest().rootElement().children()[0];
        assert.isTrue(node.expanded);
        // Re-render with the same template (simulating an update that doesn't change 'open')
        component.template = html `
      <ul role="tree">
        <li role="treeitem" open class="some-change">subtree
          <ul role="group">
            <li role="treeitem">in subtree</li>
          </ul>
        </li>
      </ul>`;
        await new Promise(resolve => setTimeout(resolve, 0));
        assert.isTrue(node.expanded, 'Node should remain expanded after re-render');
    });
    it('keeps the node collapsed after re-rendering if the user has manually collapsed it', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem" open>subtree
          <ul role="group">
            <li role="treeitem">in subtree</li>
          </ul>
        </li>
      </ul>`}></devtools-tree>`);
        const node = component.getInternalTreeOutlineForTest().rootElement().children()[0];
        assert.isTrue(node.expanded);
        node.collapse();
        assert.isFalse(node.expanded);
        // Re-render with the same template (simulating an update that doesn't change 'open')
        component.template = html `
      <ul role="tree">
        <li role="treeitem" open class="some-change">subtree
          <ul role="group">
            <li role="treeitem">in subtree</li>
          </ul>
        </li>
      </ul>`;
        await new Promise(resolve => setTimeout(resolve, 0));
        assert.isFalse(node.expanded, 'Node should remain collapsed after re-render');
    });
    it('sends a `select` event when a node is selected', async () => {
        const onSelectFirst = sinon.stub();
        const onSelectSecond = sinon.stub();
        let firstConfigElement, secondConfigElement;
        // clang-format off
        const component = await makeTree(html `
      <devtools-tree .template=${html `
        <ul role="tree">
          <li ${Lit.Directives.ref(e => { firstConfigElement = e; })}
              role="treeitem"
              @select=${onSelectFirst}>
            first node
          </li>
          <li ${Lit.Directives.ref(e => { secondConfigElement = e; })}
              role="treeitem"
              @select=${onSelectSecond}>
            second node
          </li>
        </ul>`}>
      </devtools-tree>`);
        // clang-format on
        assert.exists(firstConfigElement);
        assert.exists(secondConfigElement);
        component.getInternalTreeOutlineForTest().rootElement().lastChild()?.select();
        sinon.assert.notCalled(onSelectFirst);
        sinon.assert.calledOnce(onSelectSecond);
    });
    it('sends an `expand` event when a node is expanded or collapsed', async () => {
        const onExpand1 = sinon.stub();
        const onExpand2 = sinon.stub();
        const component = await makeTree(html `
     <devtools-tree
       .template=${html `
         <ul role="tree">
           <li @expand=${onExpand1} role="treeitem">first subtree
             <ul role="group">
               <li role="treeitem">in first subtree</li>
             </ul>
           </li>
           <li @expand=${onExpand2} role="treeitem">second subtree
             <ul role="group">
               <li role="treeitem">in second subtree</li>
             </ul>
           </li>
         </ul>`}></devtools-tree>`);
        component.getInternalTreeOutlineForTest().rootElement().lastChild()?.expand();
        sinon.assert.calledOnce(onExpand2);
        assert.deepEqual(onExpand2.args[0][0].detail, { expanded: true });
        component.getInternalTreeOutlineForTest().rootElement().lastChild()?.collapse();
        sinon.assert.calledTwice(onExpand2);
        assert.deepEqual(onExpand2.args[1][0].detail, { expanded: false });
        sinon.assert.notCalled(onExpand1);
    });
    it('applies jslog contexts to tree elements', async () => {
        const component = await makeTree(html `
      <devtools-tree
        .template=${html `
          <ul role="tree">
            <li role="treeitem" jslog-context="first">first node</li>
            <li role="treeitem" jslog-context="second">second node</li>
          </ul>
        `}></devtools-tree>`);
        assert.deepEqual(component.getInternalTreeOutlineForTest().rootElement().children().map(element => element.listItemElement.getAttribute('jslog')), [
            'TreeItem; parent: parentTreeItem; context: first; track: click, resize, keydown: ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End',
            'TreeItem; parent: parentTreeItem; context: second; track: click, resize, keydown: ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End'
        ]);
    });
    it('applies aria attributes to tree eleemnts', async () => {
        function attributes(element) {
            const attributes = {};
            for (let i = 0; i < element.listItemElement.attributes.length; ++i) {
                const attributeNode = element.listItemElement.attributes.item(i);
                if (attributeNode && attributeNode.name !== 'jslog') {
                    attributes[attributeNode.name] = attributeNode.value;
                }
            }
            return attributes;
        }
        const component = await makeTree(html `
      <devtools-tree
        .template=${html `
          <ul role="tree">
            <li role="treeitem" aria-checked="true" aria-live="off" aria-bogus="false">first node</li>
            <li role="treeitem" aria-modal="true" aria-unknown="true">second node</li>
          </ul>
        `}></devtools-tree>`);
        assert.deepEqual(component.getInternalTreeOutlineForTest().rootElement().children().map(attributes), [
            {
                'aria-checked': 'true',
                'aria-live': 'off',
                role: 'treeitem',
            },
            {
                'aria-modal': 'true',
                role: 'treeitem',
            }
        ]);
    });
    it('applies classes to tree elements', async () => {
        const component = await makeTree(html `
      <devtools-tree
        .template=${html `
          <ul role="tree">
            <li role="treeitem" class="first">first node</li>
            <li role="treeitem" class="second">second node</li>
          </ul>
        `}></devtools-tree>`);
        assert.deepEqual(component.getInternalTreeOutlineForTest().rootElement().children().map(element => [...element.listItemElement.classList]), [
            ['first'],
            ['second'],
        ]);
    });
    it('applies event listeners to tree elements', async () => {
        const onClick = sinon.stub();
        const component = await makeTree(html `
      <devtools-tree
        .template=${html `
          <ul role="tree">
            <li role="treeitem">
              <button @click=${onClick}>click me</button>
            </li>
          </ul>
        `}></devtools-tree>`);
        component.getInternalTreeOutlineForTest()
            .rootElement()
            .children()[0]
            .listItemElement.querySelector('button')
            ?.click();
        sinon.assert.calledOnce(onClick);
    });
    it('handles adding tree elements in the moddile', async () => {
        const makeTemplate = (items) => {
            return html `
        <ul role="tree">
          <li role="treeitem">node
            <ul role="group">
              ${items.map(item => html `<li role="treeitem">${item}</li>`)}
              <li role="treeitem">extra node</li>
            </ul>
          </li>
        </ul>
      `;
        };
        const component = await makeTree(html `<devtools-tree .template=${makeTemplate(['second child'])}></devtools-tree>`);
        component.template = makeTemplate(['first child', 'second child']);
        await new Promise(resolve => setTimeout(resolve, 0));
        const treeOutline = component.getInternalTreeOutlineForTest();
        const children = treeOutline.rootElement().childAt(0).children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[0].titleElement.textContent?.trim(), 'first child');
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'second child');
        assert.strictEqual(children[2].titleElement.textContent?.trim(), 'extra node');
    });
    it('marks a node as expandable even if it has empty subtree', async () => {
        const component = await makeTree(html `<devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">node
          <ul role="group">
          </ul>
        </li>
      </ul>
    `}></devtools-tree>`);
        const treeOutline = component.getInternalTreeOutlineForTest();
        assert.isTrue(treeOutline.rootElement().childAt(0).isExpandable());
    });
    it('correctly handles TreeElementWrapper nodes', async () => {
        const treeElement = new UI.TreeOutline.TreeElement('wrapper node');
        const container = document.createElement('div');
        renderElementIntoDOM(container);
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <devtools-tree-wrapper .treeElement=${treeElement}></devtools-tree-wrapper>
        <li role="treeitem">last node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        let component = container.querySelector('devtools-tree');
        let treeOutline = component.getInternalTreeOutlineForTest();
        let children = treeOutline.rootElement().children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[0].titleElement.textContent?.trim(), 'first node');
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'wrapper node');
        assert.strictEqual(children[2].titleElement.textContent?.trim(), 'last node');
        // Rerender with same wrapper but some changes in the nodes
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <devtools-tree-wrapper .treeElement=${treeElement}></devtools-tree-wrapper>
        <li role="treeitem" class=X>X node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        component = container.querySelector('devtools-tree');
        treeOutline = component.getInternalTreeOutlineForTest();
        children = treeOutline.rootElement().children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'wrapper node');
        assert.strictEqual(children[2].titleElement.textContent?.trim(), 'X node');
        // Rerender with same wrapper and the original tree
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <devtools-tree-wrapper .treeElement=${treeElement}></devtools-tree-wrapper>
        <li role="treeitem">last node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        component = container.querySelector('devtools-tree');
        treeOutline = component.getInternalTreeOutlineForTest();
        children = treeOutline.rootElement().children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'wrapper node');
        assert.strictEqual(children[2].titleElement.textContent?.trim(), 'last node');
        // Rerender with same wrapper
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <devtools-tree-wrapper .treeElement=${treeElement}></devtools-tree-wrapper>
        <li role="treeitem">last node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        component = container.querySelector('devtools-tree');
        treeOutline = component.getInternalTreeOutlineForTest();
        children = treeOutline.rootElement().children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'wrapper node');
        // Rerender with different wrapper
        const treeElement2 = new UI.TreeOutline.TreeElement('wrapper node 2');
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <devtools-tree-wrapper .treeElement=${treeElement2}></devtools-tree-wrapper>
        <li role="treeitem">last node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        component = container.querySelector('devtools-tree');
        treeOutline = component.getInternalTreeOutlineForTest();
        children = treeOutline.rootElement().children();
        assert.lengthOf(children, 3);
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'wrapper node 2');
        // Rerender with removed wrapper
        render(html `
      <devtools-tree .template=${html `
      <ul role="tree">
        <li role="treeitem">first node</li>
        <li role="treeitem">last node</li>
      </ul>`}></devtools-tree>
    `, container);
        await new Promise(resolve => setTimeout(resolve, 0));
        component = container.querySelector('devtools-tree');
        treeOutline = component.getInternalTreeOutlineForTest();
        children = treeOutline.rootElement().children();
        assert.lengthOf(children, 2);
        assert.strictEqual(children[0].titleElement.textContent?.trim(), 'first node');
        assert.strictEqual(children[1].titleElement.textContent?.trim(), 'last node');
    });
});
class TestTreeNode {
    contents;
    contentsMap;
    #children = [];
    constructor(contents, contentsMap) {
        this.contents = contents;
        this.contentsMap = contentsMap;
        assert.isUndefined(contentsMap.get(contents), 'Test expects contents to be unique');
        contentsMap.set(contents, this);
    }
    get(contents) {
        return this.contentsMap.get(contents);
    }
    static make(spec, root = new TestTreeNode('', new Map())) {
        if (typeof spec === 'string') {
            root.children().push(new TestTreeNode(spec, root.contentsMap));
            return root;
        }
        for (const key of Object.keys(spec)) {
            const child = new TestTreeNode(key, root.contentsMap);
            root.children().push(child);
            TestTreeNode.make(spec[key], child);
        }
        return root;
    }
    children() {
        return this.#children;
    }
    match(regex) {
        const matches = this.contents.matchAll(regex);
        return matches
            .map((match, matchIndexInNode) => ({
            node: this,
            matchIndexInNode,
            isPostOrderMatch: false,
        }))
            .toArray();
    }
}
describe('TreeSearch', () => {
    const tree = TestTreeNode.make({
        level1_child1: {
            level2_child1_1: 'foo bar baz',
            level2_child1_2: 'match 0',
        },
        level1_child2: {
            'match 1': 'level2_child2_1',
            level2_child2_2: 'bar',
        },
        level1_child3: 'match 2',
    });
    it('collects search results', () => {
        const search = new UI.TreeOutline.TreeSearch();
        assert.isNotOk(search.currentMatch());
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/match/gi));
        const viewStub = sinon.createStubInstance(UI.SearchableView.SearchableView);
        search.updateSearchableView(viewStub);
        sinon.assert.calledOnceWithExactly(viewStub.updateSearchMatchesCount, 3);
        sinon.assert.calledOnceWithExactly(viewStub.updateCurrentMatchIndex, 0);
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 0');
        search.reset();
        search.updateSearchableView(viewStub);
        sinon.assert.calledTwice(viewStub.updateSearchMatchesCount);
        sinon.assert.calledTwice(viewStub.updateCurrentMatchIndex);
        assert.deepEqual(viewStub.updateSearchMatchesCount.args, [[3], [0]]);
        assert.deepEqual(viewStub.updateCurrentMatchIndex.args, [[0], [0]]);
        assert.notExists(search.currentMatch());
    });
    it('supports multiple matches per node', () => {
        const search = new UI.TreeOutline.TreeSearch();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        const viewStub = sinon.createStubInstance(UI.SearchableView.SearchableView);
        search.updateSearchableView(viewStub);
        sinon.assert.calledOnceWithExactly(viewStub.updateSearchMatchesCount, 3);
        sinon.assert.calledOnceWithExactly(viewStub.updateCurrentMatchIndex, 0);
        assert.strictEqual(search.currentMatch()?.node?.contents, 'foo bar baz');
    });
    it('supports forward and backward iteration which wraps around', () => {
        const search = new UI.TreeOutline.TreeSearch();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/match/gi));
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 0');
        search.next();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 1');
        search.next();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 2');
        search.next();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 0');
        search.next();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 1');
        search.prev();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 0');
        search.prev();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 2');
        search.prev();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 1');
        search.prev();
        assert.strictEqual(search.currentMatch()?.node?.contents, 'match 0');
    });
    it('groups results by node', () => {
        const search = new UI.TreeOutline.TreeSearch();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        const node = tree.get('foo bar baz');
        assert.exists(node);
        const results = search.getResults(node);
        assert.deepEqual(results, [{ node, isPostOrderMatch: false, matchIndexInNode: 0 }, { node, isPostOrderMatch: false, matchIndexInNode: 1 }]);
    });
    it('correctly maintains the cursor position when updating the search', () => {
        const search = new UI.TreeOutline.TreeSearch();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        const firstNode = tree.get('foo bar baz');
        assert.exists(firstNode);
        const secondNode = tree.get('bar');
        assert.exists(secondNode);
        // If the current result still matches, cursor stays
        assert.deepEqual(search.currentMatch(), { node: firstNode, isPostOrderMatch: false, matchIndexInNode: 0 });
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/bar/gi));
        assert.deepEqual(search.currentMatch(), { node: firstNode, isPostOrderMatch: false, matchIndexInNode: 0 });
        search.next();
        assert.deepEqual(search.currentMatch(), { node: secondNode, isPostOrderMatch: false, matchIndexInNode: 0 });
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        assert.deepEqual(search.currentMatch(), { node: secondNode, isPostOrderMatch: false, matchIndexInNode: 0 });
        // If the current result does not match, cursor moves forward
        search.reset();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        search.next();
        assert.deepEqual(search.currentMatch(), { node: firstNode, isPostOrderMatch: false, matchIndexInNode: 1 });
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/bar/gi));
        assert.deepEqual(search.currentMatch(), { node: secondNode, isPostOrderMatch: false, matchIndexInNode: 0 });
        // If the current result does not match, cursor moves backward if moveBackward is passed
        search.reset();
        search.search(tree, false, (node, isPostOrder) => (isPostOrder && []) || node.match(/ba/gi));
        search.next();
        assert.deepEqual(search.currentMatch(), { node: firstNode, isPostOrderMatch: false, matchIndexInNode: 1 });
        search.search(tree, true, (node, isPostOrder) => (isPostOrder && []) || node.match(/bar/gi));
        assert.deepEqual(search.currentMatch(), { node: firstNode, isPostOrderMatch: false, matchIndexInNode: 0 });
    });
});
//# sourceMappingURL=Treeoutline.test.js.map