mirror of
https://github.com/coder/code-server.git
synced 2026-06-12 13:07:10 +02:00
Squashed 'lib/vscode/' content from commit e5a624b788
git-subtree-dir: lib/vscode git-subtree-split: e5a624b788d92b8d34d1392e4c4d9789406efe8f
This commit is contained in:
438
src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts
Normal file
438
src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts
Normal file
@@ -0,0 +1,438 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ITreeNode, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
interface Element {
|
||||
id: string;
|
||||
suffix?: string;
|
||||
children?: Element[];
|
||||
}
|
||||
|
||||
function find(element: Element, id: string): Element | undefined {
|
||||
if (element.id === id) {
|
||||
return element;
|
||||
}
|
||||
|
||||
if (!element.children) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const child of element.children) {
|
||||
const result = find(child, id);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
class Renderer implements ITreeRenderer<Element, void, HTMLElement> {
|
||||
readonly templateId = 'default';
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
renderElement(element: ITreeNode<Element, void>, index: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = element.element.id + (element.element.suffix || '');
|
||||
}
|
||||
disposeTemplate(templateData: HTMLElement): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
class IdentityProvider implements IIdentityProvider<Element> {
|
||||
getId(element: Element) {
|
||||
return element.id;
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualDelegate implements IListVirtualDelegate<Element> {
|
||||
getHeight() { return 20; }
|
||||
getTemplateId(element: Element): string { return 'default'; }
|
||||
}
|
||||
|
||||
class DataSource implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
return Promise.resolve(element.children || []);
|
||||
}
|
||||
}
|
||||
|
||||
class Model {
|
||||
|
||||
constructor(readonly root: Element) { }
|
||||
|
||||
get(id: string): Element {
|
||||
const result = find(this.root, id);
|
||||
|
||||
if (!result) {
|
||||
throw new Error('element not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
suite('AsyncDataTree', function () {
|
||||
|
||||
test('Collapse state should be preserved across refresh calls', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a'
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 0);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
|
||||
model.get('a').children = [
|
||||
{ id: 'aa' },
|
||||
{ id: 'ab' },
|
||||
{ id: 'ac' }
|
||||
];
|
||||
|
||||
await tree.updateChildren(model.root);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
|
||||
await tree.expand(model.get('a'));
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 4);
|
||||
|
||||
model.get('a').children = [];
|
||||
await tree.updateChildren(model.root);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
});
|
||||
|
||||
test('issue #68648', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const getChildrenCalls: string[] = [];
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
getChildrenCalls.push(element.id);
|
||||
return Promise.resolve(element.children || []);
|
||||
}
|
||||
};
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a'
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root']);
|
||||
|
||||
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
assert(tree.getNode().children[0].collapsed);
|
||||
|
||||
model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }];
|
||||
await tree.updateChildren(model.root);
|
||||
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'root']);
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(twistie.classList.contains('collapsible'));
|
||||
assert(twistie.classList.contains('collapsed'));
|
||||
assert(tree.getNode().children[0].collapsed);
|
||||
|
||||
model.get('a').children = [];
|
||||
await tree.updateChildren(model.root);
|
||||
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root']);
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
assert(tree.getNode().children[0].collapsed);
|
||||
|
||||
model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }];
|
||||
await tree.updateChildren(model.root);
|
||||
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root', 'root']);
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(twistie.classList.contains('collapsible'));
|
||||
assert(twistie.classList.contains('collapsed'));
|
||||
assert(tree.getNode().children[0].collapsed);
|
||||
});
|
||||
|
||||
test('issue #67722 - once resolved, refreshed collapsed nodes should only get children when expanded', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const getChildrenCalls: string[] = [];
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
getChildrenCalls.push(element.id);
|
||||
return Promise.resolve(element.children || []);
|
||||
}
|
||||
};
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
assert(tree.getNode(model.get('a')).collapsed);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root']);
|
||||
|
||||
await tree.expand(model.get('a'));
|
||||
assert(!tree.getNode(model.get('a')).collapsed);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
|
||||
|
||||
tree.collapse(model.get('a'));
|
||||
assert(tree.getNode(model.get('a')).collapsed);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
|
||||
|
||||
await tree.updateChildren();
|
||||
assert(tree.getNode(model.get('a')).collapsed);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'a', 'root'], 'a should not be refreshed, since it\' collapsed');
|
||||
});
|
||||
|
||||
test('resolved collapsed nodes which lose children should lose twistie as well', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
await tree.expand(model.get('a'));
|
||||
|
||||
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
assert(!tree.getNode(model.get('a')).collapsed);
|
||||
|
||||
tree.collapse(model.get('a'));
|
||||
model.get('a').children = [];
|
||||
await tree.updateChildren(model.root);
|
||||
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
assert(tree.getNode(model.get('a')).collapsed);
|
||||
});
|
||||
|
||||
test('support default collapse state per element', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const getChildrenCalls: string[] = [];
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
getChildrenCalls.push(element.id);
|
||||
return Promise.resolve(element.children || []);
|
||||
}
|
||||
};
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, {
|
||||
collapseByDefault: el => el.id !== 'a'
|
||||
});
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
assert(!tree.getNode(model.get('a')).collapsed);
|
||||
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
|
||||
});
|
||||
|
||||
test('issue #80098 - concurrent refresh and expand', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const calls: Function[] = [];
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
return new Promise(c => calls.push(() => c(element.children || [])));
|
||||
}
|
||||
};
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a', children: [{
|
||||
id: 'aa'
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
const pSetInput = tree.setInput(model.root);
|
||||
calls.pop()!(); // resolve getChildren(root)
|
||||
await pSetInput;
|
||||
|
||||
const pUpdateChildrenA = tree.updateChildren(model.get('a'));
|
||||
const pExpandA = tree.expand(model.get('a'));
|
||||
assert.equal(calls.length, 1, 'expand(a) still hasn\'t called getChildren(a)');
|
||||
|
||||
calls.pop()!();
|
||||
assert.equal(calls.length, 0, 'no pending getChildren calls');
|
||||
|
||||
await pUpdateChildrenA;
|
||||
assert.equal(calls.length, 0, 'expand(a) should not have forced a second refresh');
|
||||
|
||||
const result = await pExpandA;
|
||||
assert.equal(result, true, 'expand(a) should be done');
|
||||
});
|
||||
|
||||
test('issue #80098 - first expand should call getChildren', async () => {
|
||||
const container = document.createElement('div');
|
||||
|
||||
const calls: Function[] = [];
|
||||
const dataSource = new class implements IAsyncDataSource<Element, Element> {
|
||||
hasChildren(element: Element): boolean {
|
||||
return !!element.children && element.children.length > 0;
|
||||
}
|
||||
getChildren(element: Element): Promise<Element[]> {
|
||||
return new Promise(c => calls.push(() => c(element.children || [])));
|
||||
}
|
||||
};
|
||||
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a', children: [{
|
||||
id: 'aa'
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
const pSetInput = tree.setInput(model.root);
|
||||
calls.pop()!(); // resolve getChildren(root)
|
||||
await pSetInput;
|
||||
|
||||
const pExpandA = tree.expand(model.get('a'));
|
||||
assert.equal(calls.length, 1, 'expand(a) should\'ve called getChildren(a)');
|
||||
|
||||
let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
|
||||
assert.equal(race, 'timeout', 'expand(a) should not be yet done');
|
||||
|
||||
calls.pop()!();
|
||||
assert.equal(calls.length, 0, 'no pending getChildren calls');
|
||||
|
||||
race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
|
||||
assert.equal(race, 'expand', 'expand(a) should now be done');
|
||||
});
|
||||
|
||||
test('issue #78388 - tree should react to hasChildren toggles', async () => {
|
||||
const container = document.createElement('div');
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a'
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
|
||||
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
|
||||
model.get('a').children = [{ id: 'aa' }];
|
||||
await tree.updateChildren(model.get('a'), false);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(twistie.classList.contains('collapsible'));
|
||||
assert(twistie.classList.contains('collapsed'));
|
||||
|
||||
model.get('a').children = [];
|
||||
await tree.updateChildren(model.get('a'), false);
|
||||
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
|
||||
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
|
||||
assert(!twistie.classList.contains('collapsible'));
|
||||
assert(!twistie.classList.contains('collapsed'));
|
||||
});
|
||||
|
||||
test('issues #84569, #82629 - rerender', async () => {
|
||||
const container = document.createElement('div');
|
||||
const model = new Model({
|
||||
id: 'root',
|
||||
children: [{
|
||||
id: 'a',
|
||||
children: [{
|
||||
id: 'b',
|
||||
suffix: '1'
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
|
||||
tree.layout(200);
|
||||
|
||||
await tree.setInput(model.root);
|
||||
await tree.expand(model.get('a'));
|
||||
assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b1']);
|
||||
|
||||
const a = model.get('a');
|
||||
const b = model.get('b');
|
||||
a.children?.splice(0, 1, { id: 'b', suffix: '2' });
|
||||
|
||||
await Promise.all([
|
||||
tree.updateChildren(a, true, true),
|
||||
tree.updateChildren(b, true, true)
|
||||
]);
|
||||
|
||||
assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b2']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,431 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
|
||||
interface IResolvedCompressedTreeElement<T> extends ICompressedTreeElement<T> {
|
||||
readonly element: T;
|
||||
readonly children?: ICompressedTreeElement<T>[];
|
||||
}
|
||||
|
||||
function resolve<T>(treeElement: ICompressedTreeElement<T>): IResolvedCompressedTreeElement<T> {
|
||||
const result: any = { element: treeElement.element };
|
||||
const children = [...Iterable.map(Iterable.from(treeElement.children), resolve)];
|
||||
|
||||
if (treeElement.incompressible) {
|
||||
result.incompressible = true;
|
||||
}
|
||||
|
||||
if (children.length > 0) {
|
||||
result.children = children;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
suite('CompressedObjectTree', function () {
|
||||
|
||||
suite('compress & decompress', function () {
|
||||
|
||||
test('small', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = { element: 1 };
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> =
|
||||
{ element: { elements: [1], incompressible: false } };
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('no compression', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
{ element: 13 }
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [11], incompressible: false } },
|
||||
{ element: { elements: [12], incompressible: false } },
|
||||
{ element: { elements: [13], incompressible: false } }
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('single hierarchy', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, children: [
|
||||
{ element: 1111 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1, 11, 111, 1111], incompressible: false }
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('deep compression', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, children: [
|
||||
{ element: 1111 },
|
||||
{ element: 1112 },
|
||||
{ element: 1113 },
|
||||
{ element: 1114 },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1, 11, 111], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [1111], incompressible: false } },
|
||||
{ element: { elements: [1112], incompressible: false } },
|
||||
{ element: { elements: [1113], incompressible: false } },
|
||||
{ element: { elements: [1114], incompressible: false } },
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('double deep compression', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, children: [
|
||||
{ element: 1112 },
|
||||
{ element: 1113 },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
element: 12, children: [
|
||||
{
|
||||
element: 121, children: [
|
||||
{ element: 1212 },
|
||||
{ element: 1213 },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1], incompressible: false },
|
||||
children: [
|
||||
{
|
||||
element: { elements: [11, 111], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [1112], incompressible: false } },
|
||||
{ element: { elements: [1113], incompressible: false } },
|
||||
]
|
||||
},
|
||||
{
|
||||
element: { elements: [12, 121], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [1212], incompressible: false } },
|
||||
{ element: { elements: [1213], incompressible: false } },
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('incompressible leaf', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, children: [
|
||||
{ element: 1111, incompressible: true }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1, 11, 111], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [1111], incompressible: true } }
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('incompressible branch', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, incompressible: true, children: [
|
||||
{ element: 1111 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1, 11], incompressible: false },
|
||||
children: [
|
||||
{ element: { elements: [111, 1111], incompressible: true } }
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('incompressible chain', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{
|
||||
element: 111, incompressible: true, children: [
|
||||
{ element: 1111, incompressible: true }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1, 11], incompressible: false },
|
||||
children: [
|
||||
{
|
||||
element: { elements: [111], incompressible: true },
|
||||
children: [
|
||||
{ element: { elements: [1111], incompressible: true } }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
|
||||
test('incompressible tree', function () {
|
||||
const decompressed: ICompressedTreeElement<number> = {
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, incompressible: true, children: [
|
||||
{
|
||||
element: 111, incompressible: true, children: [
|
||||
{ element: 1111, incompressible: true }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
|
||||
element: { elements: [1], incompressible: false },
|
||||
children: [
|
||||
{
|
||||
element: { elements: [11], incompressible: true },
|
||||
children: [
|
||||
{
|
||||
element: { elements: [111], incompressible: true },
|
||||
children: [
|
||||
{ element: { elements: [1111], incompressible: true } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(resolve(compress(decompressed)), compressed);
|
||||
assert.deepEqual(resolve(decompress(compressed)), decompressed);
|
||||
});
|
||||
});
|
||||
|
||||
function toList<T>(arr: T[]): IList<T> {
|
||||
return {
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
arr.splice(start, deleteCount, ...elements);
|
||||
},
|
||||
updateElementHeight() { }
|
||||
};
|
||||
}
|
||||
|
||||
function toArray<T>(list: ITreeNode<ICompressedTreeNode<T>>[]): T[][] {
|
||||
return list.map(i => i.element.elements);
|
||||
}
|
||||
|
||||
suite('CompressedObjectTreeModel', function () {
|
||||
|
||||
test('ctor', () => {
|
||||
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
|
||||
const model = new CompressedObjectTreeModel<number>('test', toList(list));
|
||||
assert(model);
|
||||
assert.equal(list.length, 0);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('flat', () => {
|
||||
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
|
||||
const model = new CompressedObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{ element: 0 },
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[0], [1], [2]]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null, [
|
||||
{ element: 3 },
|
||||
{ element: 4 },
|
||||
{ element: 5 },
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[3], [4], [5]]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null);
|
||||
assert.deepEqual(toArray(list), []);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('nested', () => {
|
||||
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
|
||||
const model = new CompressedObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [1], [2]]);
|
||||
assert.equal(model.size, 6);
|
||||
|
||||
model.setChildren(12, [
|
||||
{ element: 120 },
|
||||
{ element: 121 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [120], [121], [1], [2]]);
|
||||
assert.equal(model.size, 8);
|
||||
|
||||
model.setChildren(0);
|
||||
assert.deepEqual(toArray(list), [[0], [1], [2]]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null);
|
||||
assert.deepEqual(toArray(list), []);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('compressed', () => {
|
||||
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
|
||||
const model = new CompressedObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{
|
||||
element: 1, children: [{
|
||||
element: 11, children: [{
|
||||
element: 111, children: [
|
||||
{ element: 1111 },
|
||||
{ element: 1112 },
|
||||
{ element: 1113 },
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[1, 11, 111], [1111], [1112], [1113]]);
|
||||
assert.equal(model.size, 6);
|
||||
|
||||
model.setChildren(11, [
|
||||
{ element: 111 },
|
||||
{ element: 112 },
|
||||
{ element: 113 },
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113]]);
|
||||
assert.equal(model.size, 5);
|
||||
|
||||
model.setChildren(113, [
|
||||
{ element: 1131 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131]]);
|
||||
assert.equal(model.size, 6);
|
||||
|
||||
model.setChildren(1131, [
|
||||
{ element: 1132 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131, 1132]]);
|
||||
assert.equal(model.size, 7);
|
||||
|
||||
model.setChildren(1131, [
|
||||
{ element: 1132 },
|
||||
{ element: 1133 },
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131], [1132], [1133]]);
|
||||
assert.equal(model.size, 8);
|
||||
});
|
||||
});
|
||||
});
|
||||
148
src/vs/base/test/browser/ui/tree/dataTree.test.ts
Normal file
148
src/vs/base/test/browser/ui/tree/dataTree.test.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ITreeNode, ITreeRenderer, IDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
|
||||
|
||||
interface E {
|
||||
value: number;
|
||||
children?: E[];
|
||||
}
|
||||
|
||||
suite('DataTree', function () {
|
||||
let tree: DataTree<E, E>;
|
||||
|
||||
const root: E = {
|
||||
value: -1,
|
||||
children: [
|
||||
{ value: 0, children: [{ value: 10 }, { value: 11 }, { value: 12 }] },
|
||||
{ value: 1 },
|
||||
{ value: 2 },
|
||||
]
|
||||
};
|
||||
|
||||
const empty: E = {
|
||||
value: -1,
|
||||
children: []
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const delegate = new class implements IListVirtualDelegate<E> {
|
||||
getHeight() { return 20; }
|
||||
getTemplateId(): string { return 'default'; }
|
||||
};
|
||||
|
||||
const renderer = new class implements ITreeRenderer<E, void, HTMLElement> {
|
||||
readonly templateId = 'default';
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
renderElement(element: ITreeNode<E, void>, index: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = `${element.element.value}`;
|
||||
}
|
||||
disposeTemplate(): void { }
|
||||
};
|
||||
|
||||
const dataSource = new class implements IDataSource<E, E> {
|
||||
getChildren(element: E): E[] {
|
||||
return element.children || [];
|
||||
}
|
||||
};
|
||||
|
||||
const identityProvider = new class implements IIdentityProvider<E> {
|
||||
getId(element: E): { toString(): string; } {
|
||||
return `${element.value}`;
|
||||
}
|
||||
};
|
||||
|
||||
tree = new DataTree<E, E>('test', container, delegate, [renderer], dataSource, {
|
||||
identityProvider
|
||||
});
|
||||
tree.layout(200);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
tree.dispose();
|
||||
});
|
||||
|
||||
test('view state is lost implicitly', () => {
|
||||
tree.setInput(root);
|
||||
|
||||
let navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 10);
|
||||
assert.equal(navigator.next()!.value, 11);
|
||||
assert.equal(navigator.next()!.value, 12);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
tree.collapse(root.children![0]);
|
||||
navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
tree.setSelection([root.children![1]]);
|
||||
tree.setFocus([root.children![2]]);
|
||||
|
||||
tree.setInput(empty);
|
||||
tree.setInput(root);
|
||||
navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 10);
|
||||
assert.equal(navigator.next()!.value, 11);
|
||||
assert.equal(navigator.next()!.value, 12);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
assert.deepEqual(tree.getSelection(), []);
|
||||
assert.deepEqual(tree.getFocus(), []);
|
||||
});
|
||||
|
||||
test('view state can be preserved', () => {
|
||||
tree.setInput(root);
|
||||
|
||||
let navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 10);
|
||||
assert.equal(navigator.next()!.value, 11);
|
||||
assert.equal(navigator.next()!.value, 12);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
tree.collapse(root.children![0]);
|
||||
navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
tree.setSelection([root.children![1]]);
|
||||
tree.setFocus([root.children![2]]);
|
||||
|
||||
const viewState = tree.getViewState();
|
||||
|
||||
tree.setInput(empty);
|
||||
tree.setInput(root, viewState);
|
||||
navigator = tree.navigate();
|
||||
assert.equal(navigator.next()!.value, 0);
|
||||
assert.equal(navigator.next()!.value, 1);
|
||||
assert.equal(navigator.next()!.value, 2);
|
||||
assert.equal(navigator.next()!, null);
|
||||
|
||||
assert.deepEqual(tree.getSelection(), [root.children![1]]);
|
||||
assert.deepEqual(tree.getFocus(), [root.children![2]]);
|
||||
});
|
||||
});
|
||||
759
src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts
Normal file
759
src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts
Normal file
@@ -0,0 +1,759 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IndexTreeModel, IIndexTreeNode, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
|
||||
function toList<T>(arr: T[]): IList<T> {
|
||||
return {
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
arr.splice(start, deleteCount, ...elements);
|
||||
},
|
||||
updateElementHeight() { }
|
||||
};
|
||||
}
|
||||
|
||||
function toArray<T>(list: ITreeNode<T>[]): T[] {
|
||||
return list.map(i => i.element);
|
||||
}
|
||||
|
||||
suite('IndexTreeModel', function () {
|
||||
|
||||
test('ctor', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
assert(model);
|
||||
assert.equal(list.length, 0);
|
||||
});
|
||||
|
||||
test('insert', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{ element: 0 },
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 3);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 1);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 1);
|
||||
assert.deepEqual(list[2].element, 2);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 1);
|
||||
});
|
||||
|
||||
test('deep insert', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 6);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 2);
|
||||
assert.deepEqual(list[2].element, 11);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 2);
|
||||
assert.deepEqual(list[3].element, 12);
|
||||
assert.deepEqual(list[3].collapsed, false);
|
||||
assert.deepEqual(list[3].depth, 2);
|
||||
assert.deepEqual(list[4].element, 1);
|
||||
assert.deepEqual(list[4].collapsed, false);
|
||||
assert.deepEqual(list[4].depth, 1);
|
||||
assert.deepEqual(list[5].element, 2);
|
||||
assert.deepEqual(list[5].collapsed, false);
|
||||
assert.deepEqual(list[5].depth, 1);
|
||||
});
|
||||
|
||||
test('deep insert collapsed', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, collapsed: true, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 3);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, true);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 1);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 1);
|
||||
assert.deepEqual(list[2].element, 2);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 1);
|
||||
});
|
||||
|
||||
test('delete', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{ element: 0 },
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 3);
|
||||
|
||||
model.splice([1], 1);
|
||||
assert.deepEqual(list.length, 2);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 2);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 1);
|
||||
|
||||
model.splice([0], 2);
|
||||
assert.deepEqual(list.length, 0);
|
||||
});
|
||||
|
||||
test('nested delete', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 6);
|
||||
|
||||
model.splice([1], 2);
|
||||
assert.deepEqual(list.length, 4);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 2);
|
||||
assert.deepEqual(list[2].element, 11);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 2);
|
||||
assert.deepEqual(list[3].element, 12);
|
||||
assert.deepEqual(list[3].collapsed, false);
|
||||
assert.deepEqual(list[3].depth, 2);
|
||||
});
|
||||
|
||||
test('deep delete', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 6);
|
||||
|
||||
model.splice([0], 1);
|
||||
assert.deepEqual(list.length, 2);
|
||||
assert.deepEqual(list[0].element, 1);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 2);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 1);
|
||||
});
|
||||
|
||||
test('hidden delete', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, collapsed: true, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 3);
|
||||
|
||||
model.splice([0, 1], 1);
|
||||
assert.deepEqual(list.length, 3);
|
||||
|
||||
model.splice([0, 0], 2);
|
||||
assert.deepEqual(list.length, 3);
|
||||
});
|
||||
|
||||
test('collapse', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 6);
|
||||
|
||||
model.setCollapsed([0], true);
|
||||
assert.deepEqual(list.length, 3);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, true);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 1);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 1);
|
||||
assert.deepEqual(list[2].element, 2);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 1);
|
||||
});
|
||||
|
||||
test('expand', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, collapsed: true, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 3);
|
||||
|
||||
model.setCollapsed([0], false);
|
||||
assert.deepEqual(list.length, 6);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[0].depth, 1);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
assert.deepEqual(list[1].depth, 2);
|
||||
assert.deepEqual(list[2].element, 11);
|
||||
assert.deepEqual(list[2].collapsed, false);
|
||||
assert.deepEqual(list[2].depth, 2);
|
||||
assert.deepEqual(list[3].element, 12);
|
||||
assert.deepEqual(list[3].collapsed, false);
|
||||
assert.deepEqual(list[3].depth, 2);
|
||||
assert.deepEqual(list[4].element, 1);
|
||||
assert.deepEqual(list[4].collapsed, false);
|
||||
assert.deepEqual(list[4].depth, 1);
|
||||
assert.deepEqual(list[5].element, 2);
|
||||
assert.deepEqual(list[5].collapsed, false);
|
||||
assert.deepEqual(list[5].depth, 1);
|
||||
});
|
||||
|
||||
test('collapse should recursively adjust visible count', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 1, children: [
|
||||
{
|
||||
element: 11, children: [
|
||||
{ element: 111 }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
element: 2, children: [
|
||||
{ element: 21 }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 5);
|
||||
assert.deepEqual(toArray(list), [1, 11, 111, 2, 21]);
|
||||
|
||||
model.setCollapsed([0, 0], true);
|
||||
assert.deepEqual(list.length, 4);
|
||||
assert.deepEqual(toArray(list), [1, 11, 2, 21]);
|
||||
|
||||
model.setCollapsed([1], true);
|
||||
assert.deepEqual(list.length, 3);
|
||||
assert.deepEqual(toArray(list), [1, 11, 2]);
|
||||
});
|
||||
|
||||
test('setCollapsible', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 2);
|
||||
|
||||
model.setCollapsible([0], false);
|
||||
assert.deepEqual(list.length, 2);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, false);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsible, false);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
|
||||
assert.deepEqual(model.setCollapsed([0], true), false);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, false);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsible, false);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
|
||||
assert.deepEqual(model.setCollapsed([0], false), false);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, false);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsible, false);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
|
||||
model.setCollapsible([0], true);
|
||||
assert.deepEqual(list.length, 2);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, true);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsible, false);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
|
||||
assert.deepEqual(model.setCollapsed([0], true), true);
|
||||
assert.deepEqual(list.length, 1);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, true);
|
||||
assert.deepEqual(list[0].collapsed, true);
|
||||
|
||||
assert.deepEqual(model.setCollapsed([0], false), true);
|
||||
assert.deepEqual(list[0].element, 0);
|
||||
assert.deepEqual(list[0].collapsible, true);
|
||||
assert.deepEqual(list[0].collapsed, false);
|
||||
assert.deepEqual(list[1].element, 10);
|
||||
assert.deepEqual(list[1].collapsible, false);
|
||||
assert.deepEqual(list[1].collapsed, false);
|
||||
});
|
||||
|
||||
test('simple filter', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const filter = new class implements ITreeFilter<number> {
|
||||
filter(element: number): TreeVisibility {
|
||||
return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 1 },
|
||||
{ element: 2 },
|
||||
{ element: 3 },
|
||||
{ element: 4 },
|
||||
{ element: 5 },
|
||||
{ element: 6 },
|
||||
{ element: 7 }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 4);
|
||||
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
|
||||
|
||||
model.setCollapsed([0], true);
|
||||
assert.deepEqual(toArray(list), [0]);
|
||||
|
||||
model.setCollapsed([0], false);
|
||||
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
|
||||
});
|
||||
|
||||
test('recursive filter on initial model', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const filter = new class implements ITreeFilter<number> {
|
||||
filter(element: number): TreeVisibility {
|
||||
return element === 0 ? TreeVisibility.Recurse : TreeVisibility.Hidden;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), []);
|
||||
});
|
||||
|
||||
test('refilter', function () {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
let shouldFilter = false;
|
||||
const filter = new class implements ITreeFilter<number> {
|
||||
filter(element: number): TreeVisibility {
|
||||
return (!shouldFilter || element % 2 === 0) ? TreeVisibility.Visible : TreeVisibility.Hidden;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 1 },
|
||||
{ element: 2 },
|
||||
{ element: 3 },
|
||||
{ element: 4 },
|
||||
{ element: 5 },
|
||||
{ element: 6 },
|
||||
{ element: 7 }
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
|
||||
shouldFilter = true;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
|
||||
|
||||
shouldFilter = false;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
});
|
||||
|
||||
test('recursive filter', function () {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let query = new RegExp('');
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string): TreeVisibility {
|
||||
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 'vscode', children: [
|
||||
{ element: '.build' },
|
||||
{ element: 'git' },
|
||||
{
|
||||
element: 'github', children: [
|
||||
{ element: 'calendar.yml' },
|
||||
{ element: 'endgame' },
|
||||
{ element: 'build.js' },
|
||||
]
|
||||
},
|
||||
{
|
||||
element: 'build', children: [
|
||||
{ element: 'lib' },
|
||||
{ element: 'gulpfile.js' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 10);
|
||||
|
||||
query = /build/;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['vscode', '.build', 'github', 'build.js', 'build']);
|
||||
|
||||
model.setCollapsed([0], true);
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
|
||||
model.setCollapsed([0], false);
|
||||
assert.deepEqual(toArray(list), ['vscode', '.build', 'github', 'build.js', 'build']);
|
||||
});
|
||||
|
||||
test('recursive filter with collapse', function () {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let query = new RegExp('');
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string): TreeVisibility {
|
||||
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 'vscode', children: [
|
||||
{ element: '.build' },
|
||||
{ element: 'git' },
|
||||
{
|
||||
element: 'github', children: [
|
||||
{ element: 'calendar.yml' },
|
||||
{ element: 'endgame' },
|
||||
{ element: 'build.js' },
|
||||
]
|
||||
},
|
||||
{
|
||||
element: 'build', children: [
|
||||
{ element: 'lib' },
|
||||
{ element: 'gulpfile.js' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(list.length, 10);
|
||||
|
||||
query = /gulp/;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['vscode', 'build', 'gulpfile.js']);
|
||||
|
||||
model.setCollapsed([0, 3], true);
|
||||
assert.deepEqual(toArray(list), ['vscode', 'build']);
|
||||
|
||||
model.setCollapsed([0], true);
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
});
|
||||
|
||||
test('recursive filter while collapsed', function () {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let query = new RegExp('');
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string): TreeVisibility {
|
||||
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 'vscode', collapsed: true, children: [
|
||||
{ element: '.build' },
|
||||
{ element: 'git' },
|
||||
{
|
||||
element: 'github', children: [
|
||||
{ element: 'calendar.yml' },
|
||||
{ element: 'endgame' },
|
||||
{ element: 'build.js' },
|
||||
]
|
||||
},
|
||||
{
|
||||
element: 'build', children: [
|
||||
{ element: 'lib' },
|
||||
{ element: 'gulpfile.js' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
|
||||
query = /gulp/;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
|
||||
model.setCollapsed([0], false);
|
||||
assert.deepEqual(toArray(list), ['vscode', 'build', 'gulpfile.js']);
|
||||
|
||||
model.setCollapsed([0], true);
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
|
||||
query = new RegExp('');
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['vscode']);
|
||||
|
||||
model.setCollapsed([0], false);
|
||||
assert.deepEqual(list.length, 10);
|
||||
});
|
||||
|
||||
suite('getNodeLocation', function () {
|
||||
|
||||
test('simple', function () {
|
||||
const list: IIndexTreeNode<number>[] = [];
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1);
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(model.getNodeLocation(list[0]), [0]);
|
||||
assert.deepEqual(model.getNodeLocation(list[1]), [0, 0]);
|
||||
assert.deepEqual(model.getNodeLocation(list[2]), [0, 1]);
|
||||
assert.deepEqual(model.getNodeLocation(list[3]), [0, 2]);
|
||||
assert.deepEqual(model.getNodeLocation(list[4]), [1]);
|
||||
assert.deepEqual(model.getNodeLocation(list[5]), [2]);
|
||||
});
|
||||
|
||||
test('with filter', function () {
|
||||
const list: IIndexTreeNode<number>[] = [];
|
||||
const filter = new class implements ITreeFilter<number> {
|
||||
filter(element: number): TreeVisibility {
|
||||
return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden;
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 1 },
|
||||
{ element: 2 },
|
||||
{ element: 3 },
|
||||
{ element: 4 },
|
||||
{ element: 5 },
|
||||
{ element: 6 },
|
||||
{ element: 7 }
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
assert.deepEqual(model.getNodeLocation(list[0]), [0]);
|
||||
assert.deepEqual(model.getNodeLocation(list[1]), [0, 1]);
|
||||
assert.deepEqual(model.getNodeLocation(list[2]), [0, 3]);
|
||||
assert.deepEqual(model.getNodeLocation(list[3]), [0, 5]);
|
||||
});
|
||||
});
|
||||
|
||||
test('refilter with filtered out nodes', function () {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let query = new RegExp('');
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string): boolean {
|
||||
return query.test(element);
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{ element: 'silver' },
|
||||
{ element: 'gold' },
|
||||
{ element: 'platinum' }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), ['silver', 'gold', 'platinum']);
|
||||
|
||||
query = /platinum/;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['platinum']);
|
||||
|
||||
model.splice([0], Number.POSITIVE_INFINITY, [
|
||||
{ element: 'silver' },
|
||||
{ element: 'gold' },
|
||||
{ element: 'platinum' }
|
||||
]);
|
||||
assert.deepEqual(toArray(list), ['platinum']);
|
||||
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['platinum']);
|
||||
});
|
||||
|
||||
test('explicit hidden nodes should have renderNodeCount == 0, issue #83211', function () {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let query = new RegExp('');
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string): boolean {
|
||||
return query.test(element);
|
||||
}
|
||||
};
|
||||
|
||||
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
|
||||
|
||||
model.splice([0], 0, [
|
||||
{ element: 'a', children: [{ element: 'aa' }] },
|
||||
{ element: 'b', children: [{ element: 'bb' }] }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), ['a', 'aa', 'b', 'bb']);
|
||||
assert.deepEqual(model.getListIndex([0]), 0);
|
||||
assert.deepEqual(model.getListIndex([0, 0]), 1);
|
||||
assert.deepEqual(model.getListIndex([1]), 2);
|
||||
assert.deepEqual(model.getListIndex([1, 0]), 3);
|
||||
|
||||
query = /b/;
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), ['b', 'bb']);
|
||||
assert.deepEqual(model.getListIndex([0]), -1);
|
||||
assert.deepEqual(model.getListIndex([0, 0]), -1);
|
||||
assert.deepEqual(model.getListIndex([1]), 0);
|
||||
assert.deepEqual(model.getListIndex([1, 0]), 1);
|
||||
});
|
||||
});
|
||||
375
src/vs/base/test/browser/ui/tree/objectTree.test.ts
Normal file
375
src/vs/base/test/browser/ui/tree/objectTree.test.ts
Normal file
@@ -0,0 +1,375 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { ObjectTree, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
|
||||
|
||||
suite('ObjectTree', function () {
|
||||
suite('TreeNavigator', function () {
|
||||
let tree: ObjectTree<number>;
|
||||
let filter = (_: number) => true;
|
||||
|
||||
setup(() => {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const delegate = new class implements IListVirtualDelegate<number> {
|
||||
getHeight() { return 20; }
|
||||
getTemplateId(): string { return 'default'; }
|
||||
};
|
||||
|
||||
const renderer = new class implements ITreeRenderer<number, void, HTMLElement> {
|
||||
readonly templateId = 'default';
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
renderElement(element: ITreeNode<number, void>, index: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = `${element.element}`;
|
||||
}
|
||||
disposeTemplate(): void { }
|
||||
};
|
||||
|
||||
tree = new ObjectTree<number>('test', container, delegate, [renderer], { filter: { filter: (el) => filter(el) } });
|
||||
tree.layout(200);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
tree.dispose();
|
||||
filter = (_: number) => true;
|
||||
});
|
||||
|
||||
test('should be able to navigate', () => {
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
const navigator = tree.navigate();
|
||||
|
||||
assert.equal(navigator.current(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.current(), 0);
|
||||
assert.equal(navigator.next(), 10);
|
||||
assert.equal(navigator.current(), 10);
|
||||
assert.equal(navigator.next(), 11);
|
||||
assert.equal(navigator.current(), 11);
|
||||
assert.equal(navigator.next(), 12);
|
||||
assert.equal(navigator.current(), 12);
|
||||
assert.equal(navigator.next(), 1);
|
||||
assert.equal(navigator.current(), 1);
|
||||
assert.equal(navigator.next(), 2);
|
||||
assert.equal(navigator.current(), 2);
|
||||
assert.equal(navigator.previous(), 1);
|
||||
assert.equal(navigator.current(), 1);
|
||||
assert.equal(navigator.previous(), 12);
|
||||
assert.equal(navigator.previous(), 11);
|
||||
assert.equal(navigator.previous(), 10);
|
||||
assert.equal(navigator.previous(), 0);
|
||||
assert.equal(navigator.previous(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.next(), 10);
|
||||
assert.equal(navigator.first(), 0);
|
||||
assert.equal(navigator.last(), 2);
|
||||
});
|
||||
|
||||
test('should skip collapsed nodes', () => {
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 0, collapsed: true, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
const navigator = tree.navigate();
|
||||
|
||||
assert.equal(navigator.current(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.next(), 1);
|
||||
assert.equal(navigator.next(), 2);
|
||||
assert.equal(navigator.next(), null);
|
||||
assert.equal(navigator.previous(), 2);
|
||||
assert.equal(navigator.previous(), 1);
|
||||
assert.equal(navigator.previous(), 0);
|
||||
assert.equal(navigator.previous(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.first(), 0);
|
||||
assert.equal(navigator.last(), 2);
|
||||
});
|
||||
|
||||
test('should skip filtered elements', () => {
|
||||
filter = el => el % 2 === 0;
|
||||
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
const navigator = tree.navigate();
|
||||
|
||||
assert.equal(navigator.current(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.next(), 10);
|
||||
assert.equal(navigator.next(), 12);
|
||||
assert.equal(navigator.next(), 2);
|
||||
assert.equal(navigator.next(), null);
|
||||
assert.equal(navigator.previous(), 2);
|
||||
assert.equal(navigator.previous(), 12);
|
||||
assert.equal(navigator.previous(), 10);
|
||||
assert.equal(navigator.previous(), 0);
|
||||
assert.equal(navigator.previous(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.next(), 10);
|
||||
assert.equal(navigator.first(), 0);
|
||||
assert.equal(navigator.last(), 2);
|
||||
});
|
||||
|
||||
test('should be able to start from node', () => {
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
const navigator = tree.navigate(1);
|
||||
|
||||
assert.equal(navigator.current(), 1);
|
||||
assert.equal(navigator.next(), 2);
|
||||
assert.equal(navigator.current(), 2);
|
||||
assert.equal(navigator.previous(), 1);
|
||||
assert.equal(navigator.current(), 1);
|
||||
assert.equal(navigator.previous(), 12);
|
||||
assert.equal(navigator.previous(), 11);
|
||||
assert.equal(navigator.previous(), 10);
|
||||
assert.equal(navigator.previous(), 0);
|
||||
assert.equal(navigator.previous(), null);
|
||||
assert.equal(navigator.next(), 0);
|
||||
assert.equal(navigator.next(), 10);
|
||||
assert.equal(navigator.first(), 0);
|
||||
assert.equal(navigator.last(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
test('traits are preserved according to string identity', function () {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const delegate = new class implements IListVirtualDelegate<number> {
|
||||
getHeight() { return 20; }
|
||||
getTemplateId(): string { return 'default'; }
|
||||
};
|
||||
|
||||
const renderer = new class implements ITreeRenderer<number, void, HTMLElement> {
|
||||
readonly templateId = 'default';
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
renderElement(element: ITreeNode<number, void>, index: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = `${element.element}`;
|
||||
}
|
||||
disposeTemplate(): void { }
|
||||
};
|
||||
|
||||
const identityProvider = new class implements IIdentityProvider<number> {
|
||||
getId(element: number): { toString(): string; } {
|
||||
return `${element % 100}`;
|
||||
}
|
||||
};
|
||||
|
||||
const tree = new ObjectTree<number>('test', container, delegate, [renderer], { identityProvider });
|
||||
tree.layout(200);
|
||||
|
||||
tree.setChildren(null, [{ element: 0 }, { element: 1 }, { element: 2 }, { element: 3 }]);
|
||||
tree.setFocus([1]);
|
||||
assert.deepStrictEqual(tree.getFocus(), [1]);
|
||||
|
||||
tree.setChildren(null, [{ element: 100 }, { element: 101 }, { element: 102 }, { element: 103 }]);
|
||||
assert.deepStrictEqual(tree.getFocus(), [101]);
|
||||
});
|
||||
});
|
||||
|
||||
function toArray(list: NodeList): Node[] {
|
||||
const result: Node[] = [];
|
||||
list.forEach(node => result.push(node));
|
||||
return result;
|
||||
}
|
||||
|
||||
suite('CompressibleObjectTree', function () {
|
||||
|
||||
class Delegate implements IListVirtualDelegate<number> {
|
||||
getHeight() { return 20; }
|
||||
getTemplateId(): string { return 'default'; }
|
||||
}
|
||||
|
||||
class Renderer implements ICompressibleTreeRenderer<number, void, HTMLElement> {
|
||||
readonly templateId = 'default';
|
||||
renderTemplate(container: HTMLElement): HTMLElement {
|
||||
return container;
|
||||
}
|
||||
renderElement(node: ITreeNode<number, void>, _: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = `${node.element}`;
|
||||
}
|
||||
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<number>, void>, _: number, templateData: HTMLElement): void {
|
||||
templateData.textContent = `${node.element.elements.join('/')}`;
|
||||
}
|
||||
disposeTemplate(): void { }
|
||||
}
|
||||
|
||||
test('empty', function () {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
|
||||
tree.layout(200);
|
||||
|
||||
const rows = toArray(container.querySelectorAll('.monaco-tl-contents'));
|
||||
assert.equal(rows.length, 0);
|
||||
});
|
||||
|
||||
test('simple', function () {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
|
||||
tree.layout(200);
|
||||
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
const rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['0', '10', '11', '12', '1', '2']);
|
||||
});
|
||||
|
||||
test('compressed', () => {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
|
||||
tree.layout(200);
|
||||
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 1, children: [{
|
||||
element: 11, children: [{
|
||||
element: 111, children: [
|
||||
{ element: 1111 },
|
||||
{ element: 1112 },
|
||||
{ element: 1113 },
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]);
|
||||
|
||||
let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
|
||||
|
||||
tree.setChildren(11, [
|
||||
{ element: 111 },
|
||||
{ element: 112 },
|
||||
{ element: 113 },
|
||||
]);
|
||||
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11', '111', '112', '113']);
|
||||
|
||||
tree.setChildren(113, [
|
||||
{ element: 1131 }
|
||||
]);
|
||||
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131']);
|
||||
|
||||
tree.setChildren(1131, [
|
||||
{ element: 1132 }
|
||||
]);
|
||||
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131/1132']);
|
||||
|
||||
tree.setChildren(1131, [
|
||||
{ element: 1132 },
|
||||
{ element: 1133 },
|
||||
]);
|
||||
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131', '1132', '1133']);
|
||||
});
|
||||
|
||||
test('enableCompression', () => {
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
|
||||
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
|
||||
tree.layout(200);
|
||||
|
||||
tree.setChildren(null, [
|
||||
{
|
||||
element: 1, children: [{
|
||||
element: 11, children: [{
|
||||
element: 111, children: [
|
||||
{ element: 1111 },
|
||||
{ element: 1112 },
|
||||
{ element: 1113 },
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]);
|
||||
|
||||
let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
|
||||
|
||||
tree.updateOptions({ compressionEnabled: false });
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1', '11', '111', '1111', '1112', '1113']);
|
||||
|
||||
tree.updateOptions({ compressionEnabled: true });
|
||||
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
|
||||
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
|
||||
});
|
||||
});
|
||||
276
src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts
Normal file
276
src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
|
||||
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
|
||||
|
||||
function toList<T>(arr: T[]): IList<T> {
|
||||
return {
|
||||
splice(start: number, deleteCount: number, elements: T[]): void {
|
||||
// console.log(`splice (${start}, ${deleteCount}, ${elements.length} [${elements.join(', ')}] )`); // debugging
|
||||
arr.splice(start, deleteCount, ...elements);
|
||||
},
|
||||
updateElementHeight() { }
|
||||
};
|
||||
}
|
||||
|
||||
function toArray<T>(list: ITreeNode<T>[]): T[] {
|
||||
return list.map(i => i.element);
|
||||
}
|
||||
|
||||
suite('ObjectTreeModel', function () {
|
||||
|
||||
test('ctor', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list));
|
||||
assert(model);
|
||||
assert.equal(list.length, 0);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('flat', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{ element: 0 },
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0, 1, 2]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null, [
|
||||
{ element: 3 },
|
||||
{ element: 4 },
|
||||
{ element: 5 },
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [3, 4, 5]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null);
|
||||
assert.deepEqual(toArray(list), []);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('nested', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10 },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0, 10, 11, 12, 1, 2]);
|
||||
assert.equal(model.size, 6);
|
||||
|
||||
model.setChildren(12, [
|
||||
{ element: 120 },
|
||||
{ element: 121 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0, 10, 11, 12, 120, 121, 1, 2]);
|
||||
assert.equal(model.size, 8);
|
||||
|
||||
model.setChildren(0);
|
||||
assert.deepEqual(toArray(list), [0, 1, 2]);
|
||||
assert.equal(model.size, 3);
|
||||
|
||||
model.setChildren(null);
|
||||
assert.deepEqual(toArray(list), []);
|
||||
assert.equal(model.size, 0);
|
||||
});
|
||||
|
||||
test('setChildren on collapsed node', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{ element: 0, collapsed: true }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0]);
|
||||
|
||||
model.setChildren(0, [
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0]);
|
||||
|
||||
model.setCollapsed(0, false);
|
||||
assert.deepEqual(toArray(list), [0, 1, 2]);
|
||||
});
|
||||
|
||||
test('setChildren on expanded, unrevealed node', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list));
|
||||
|
||||
model.setChildren(null, [
|
||||
{
|
||||
element: 1, collapsed: true, children: [
|
||||
{ element: 11, collapsed: false }
|
||||
]
|
||||
},
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [1, 2]);
|
||||
|
||||
model.setChildren(11, [
|
||||
{ element: 111 },
|
||||
{ element: 112 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [1, 2]);
|
||||
|
||||
model.setCollapsed(1, false);
|
||||
assert.deepEqual(toArray(list), [1, 11, 111, 112, 2]);
|
||||
});
|
||||
|
||||
test('collapse state is preserved with strict identity', () => {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
const model = new ObjectTreeModel<string>('test', toList(list), { collapseByDefault: true });
|
||||
const data = [{ element: 'father', children: [{ element: 'child' }] }];
|
||||
|
||||
model.setChildren(null, data);
|
||||
assert.deepEqual(toArray(list), ['father']);
|
||||
|
||||
model.setCollapsed('father', false);
|
||||
assert.deepEqual(toArray(list), ['father', 'child']);
|
||||
|
||||
model.setChildren(null, data);
|
||||
assert.deepEqual(toArray(list), ['father', 'child']);
|
||||
|
||||
const data2 = [{ element: 'father', children: [{ element: 'child' }] }, { element: 'uncle' }];
|
||||
model.setChildren(null, data2);
|
||||
assert.deepEqual(toArray(list), ['father', 'child', 'uncle']);
|
||||
|
||||
model.setChildren(null, [{ element: 'uncle' }]);
|
||||
assert.deepEqual(toArray(list), ['uncle']);
|
||||
|
||||
model.setChildren(null, data2);
|
||||
assert.deepEqual(toArray(list), ['father', 'uncle']);
|
||||
|
||||
model.setChildren(null, data);
|
||||
assert.deepEqual(toArray(list), ['father']);
|
||||
});
|
||||
|
||||
test('sorter', () => {
|
||||
let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1;
|
||||
|
||||
const list: ITreeNode<string>[] = [];
|
||||
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
|
||||
const data = [
|
||||
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
|
||||
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
|
||||
{ element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] },
|
||||
];
|
||||
|
||||
model.setChildren(null, data);
|
||||
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);
|
||||
});
|
||||
|
||||
test('resort', () => {
|
||||
let compare: (a: string, b: string) => number = () => 0;
|
||||
|
||||
const list: ITreeNode<string>[] = [];
|
||||
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
|
||||
const data = [
|
||||
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
|
||||
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
|
||||
{ element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] },
|
||||
];
|
||||
|
||||
model.setChildren(null, data);
|
||||
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric']);
|
||||
|
||||
// lexicographical
|
||||
compare = (a, b) => a < b ? -1 : 1;
|
||||
|
||||
// non-recursive
|
||||
model.resort(null, false);
|
||||
assert.deepEqual(toArray(list), ['airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric', 'cars', 'sedan', 'convertible', 'compact']);
|
||||
|
||||
// recursive
|
||||
model.resort();
|
||||
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);
|
||||
|
||||
// reverse
|
||||
compare = (a, b) => a < b ? 1 : -1;
|
||||
|
||||
// scoped
|
||||
model.resort('cars');
|
||||
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'sedan', 'convertible', 'compact']);
|
||||
|
||||
// recursive
|
||||
model.resort();
|
||||
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'bicycles', 'mountain', 'electric', 'dutch', 'airplanes', 'passenger', 'jet']);
|
||||
});
|
||||
|
||||
test('expandTo', () => {
|
||||
const list: ITreeNode<number>[] = [];
|
||||
const model = new ObjectTreeModel<number>('test', toList(list), { collapseByDefault: true });
|
||||
|
||||
model.setChildren(null, [
|
||||
{
|
||||
element: 0, children: [
|
||||
{ element: 10, children: [{ element: 100, children: [{ element: 1000 }] }] },
|
||||
{ element: 11 },
|
||||
{ element: 12 },
|
||||
]
|
||||
},
|
||||
{ element: 1 },
|
||||
{ element: 2 }
|
||||
]);
|
||||
|
||||
assert.deepEqual(toArray(list), [0, 1, 2]);
|
||||
model.expandTo(1000);
|
||||
assert.deepEqual(toArray(list), [0, 10, 100, 1000, 11, 12, 1, 2]);
|
||||
});
|
||||
|
||||
test('issue #95641', () => {
|
||||
const list: ITreeNode<string>[] = [];
|
||||
let fn = (_: string) => true;
|
||||
const filter = new class implements ITreeFilter<string> {
|
||||
filter(element: string, parentVisibility: TreeVisibility): TreeVisibility {
|
||||
if (element === 'file') {
|
||||
return TreeVisibility.Recurse;
|
||||
}
|
||||
|
||||
return fn(element) ? TreeVisibility.Visible : parentVisibility;
|
||||
}
|
||||
};
|
||||
const model = new ObjectTreeModel<string>('test', toList(list), { filter });
|
||||
|
||||
model.setChildren(null, [{ element: 'file', children: [{ element: 'hello' }] }]);
|
||||
assert.deepEqual(toArray(list), ['file', 'hello']);
|
||||
|
||||
fn = (el: string) => el === 'world';
|
||||
model.refilter();
|
||||
assert.deepEqual(toArray(list), []);
|
||||
|
||||
model.setChildren('file', [{ element: 'world' }]);
|
||||
assert.deepEqual(toArray(list), ['file', 'world']);
|
||||
|
||||
model.setChildren('file', [{ element: 'hello' }]);
|
||||
assert.deepEqual(toArray(list), []);
|
||||
|
||||
model.setChildren('file', [{ element: 'world' }]);
|
||||
assert.deepEqual(toArray(list), ['file', 'world']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user