mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 02:02:35 -05:00
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
741
src/vs/workbench/test/browser/api/extHostTreeViews.test.ts
Normal file
741
src/vs/workbench/test/browser/api/extHostTreeViews.test.ts
Normal file
@@ -0,0 +1,741 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TreeDataProvider, TreeItem } from 'vscode';
|
||||
import { TestRPCProtocol } from './testRPCProtocol';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import type { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
suite('ExtHostTreeView', function () {
|
||||
|
||||
class RecordingShape extends mock<MainThreadTreeViewsShape>() {
|
||||
|
||||
onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>();
|
||||
|
||||
$registerTreeViewDataProvider(treeViewId: string): void {
|
||||
}
|
||||
|
||||
$refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
|
||||
return Promise.resolve(null).then(() => {
|
||||
this.onRefresh.fire(itemsToRefresh);
|
||||
});
|
||||
}
|
||||
|
||||
$reveal(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let testObject: ExtHostTreeViews;
|
||||
let target: RecordingShape;
|
||||
let onDidChangeTreeNode: Emitter<{ key: string } | undefined>;
|
||||
let onDidChangeTreeNodeWithId: Emitter<{ key: string }>;
|
||||
let tree: { [key: string]: any };
|
||||
let labels: { [key: string]: string };
|
||||
let nodes: { [key: string]: { key: string } };
|
||||
|
||||
setup(() => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ab': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
|
||||
labels = {};
|
||||
nodes = {};
|
||||
|
||||
let rpcProtocol = new TestRPCProtocol();
|
||||
// Use IInstantiationService to get typechecking when instantiating
|
||||
let inst: IInstantiationService;
|
||||
{
|
||||
let instantiationService = new TestInstantiationService();
|
||||
inst = instantiationService;
|
||||
}
|
||||
|
||||
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
|
||||
target = new RecordingShape();
|
||||
testObject = new ExtHostTreeViews(target, new ExtHostCommands(
|
||||
rpcProtocol,
|
||||
new NullLogService()
|
||||
), new NullLogService());
|
||||
onDidChangeTreeNode = new Emitter<{ key: string } | undefined>();
|
||||
onDidChangeTreeNodeWithId = new Emitter<{ key: string }>();
|
||||
testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
testObject.createTreeView('testNodeWithHighlightsTreeProvider', { treeDataProvider: aNodeWithHighlightedLabelTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
|
||||
return loadCompleteTree('testNodeTreeProvider');
|
||||
});
|
||||
|
||||
test('construct node tree', () => {
|
||||
return testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a', '0/0:b']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/0:ab']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:aa').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:ab').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:b/0:ba', '0/0:b/0:bb']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:ba').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:bb').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('construct id tree', () => {
|
||||
return testObject.$getChildren('testNodeWithIdTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/a', '1/b']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/aa', '1/ab']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/aa').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ab').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/b')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/ba', '1/bb']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ba').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/bb').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('construct highlights tree', () => {
|
||||
return testObject.$getChildren('testNodeWithHighlightsTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(removeUnsetKeys(elements), [{
|
||||
handle: '1/a',
|
||||
label: { label: 'a', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
}, {
|
||||
handle: '1/b',
|
||||
label: { label: 'b', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
}]);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/a')
|
||||
.then(children => {
|
||||
assert.deepEqual(removeUnsetKeys(children), [{
|
||||
handle: '1/aa',
|
||||
parentHandle: '1/a',
|
||||
label: { label: 'aa', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}, {
|
||||
handle: '1/ab',
|
||||
parentHandle: '1/a',
|
||||
label: { label: 'ab', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/b')
|
||||
.then(children => {
|
||||
assert.deepEqual(removeUnsetKeys(children), [{
|
||||
handle: '1/ba',
|
||||
parentHandle: '1/b',
|
||||
label: { label: 'ba', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}, {
|
||||
handle: '1/bb',
|
||||
parentHandle: '1/b',
|
||||
label: { label: 'bb', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('error is thrown if id is not unique', (done) => {
|
||||
tree['a'] = {
|
||||
'aa': {},
|
||||
};
|
||||
tree['b'] = {
|
||||
'aa': {},
|
||||
'ba': {}
|
||||
};
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/a', '1/b']);
|
||||
return testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
|
||||
.then(() => testObject.$getChildren('testNodeWithIdTreeProvider', '1/b'))
|
||||
.then(() => { assert.fail('Should fail with duplicate id'); done(); }, () => done());
|
||||
});
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('refresh root', function (done) {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('refresh a parent node', () => {
|
||||
return new Promise((c, e) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
c(undefined);
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh a leaf node', function (done) {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b/0:bb'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b/0:bb']), {
|
||||
handle: '0/0:b/0:bb',
|
||||
parentHandle: '0/0:b',
|
||||
label: { label: 'bb' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
});
|
||||
|
||||
async function runWithEventMerging(action: (resolve: () => void) => void) {
|
||||
await new Promise((resolve) => {
|
||||
let subscription: IDisposable | undefined = undefined;
|
||||
subscription = target.onRefresh.event(() => {
|
||||
subscription!.dispose();
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
await new Promise(action);
|
||||
}
|
||||
|
||||
test('refresh parent and child node trigger refresh only on parent - scenario 1', async () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b', '0/0:a/0:aa'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
|
||||
handle: '0/0:a/0:aa',
|
||||
parentHandle: '0/0:a',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('aa'));
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh parent and child node trigger refresh only on parent - scenario 2', async () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a/0:aa', '0/0:b'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
|
||||
handle: '0/0:a/0:aa',
|
||||
parentHandle: '0/0:a',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
onDidChangeTreeNode.fire(getNode('aa'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh an element for label change', function (done) {
|
||||
labels['a'] = 'aa';
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a']), {
|
||||
handle: '0/0:aa',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on roots', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on elements', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on unknown elements', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('g'));
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on unknown elements and root', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('g'));
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on elements and root', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('generate unique handles from labels by escaping them', (done) => {
|
||||
tree = {
|
||||
'a/0:b': {}
|
||||
};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:a//0:b']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('tree with duplicate labels', (done) => {
|
||||
|
||||
const dupItems = {
|
||||
'adup1': 'c',
|
||||
'adup2': 'g',
|
||||
'bdup1': 'e',
|
||||
'hdup1': 'i',
|
||||
'hdup2': 'l',
|
||||
'jdup1': 'k'
|
||||
};
|
||||
|
||||
labels['c'] = 'a';
|
||||
labels['e'] = 'b';
|
||||
labels['g'] = 'a';
|
||||
labels['i'] = 'h';
|
||||
labels['l'] = 'h';
|
||||
labels['k'] = 'j';
|
||||
|
||||
tree[dupItems['adup1']] = {};
|
||||
tree['d'] = {};
|
||||
|
||||
const bdup1Tree: { [key: string]: any } = {};
|
||||
bdup1Tree['h'] = {};
|
||||
bdup1Tree[dupItems['hdup1']] = {};
|
||||
bdup1Tree['j'] = {};
|
||||
bdup1Tree[dupItems['jdup1']] = {};
|
||||
bdup1Tree[dupItems['hdup2']] = {};
|
||||
|
||||
tree[dupItems['bdup1']] = bdup1Tree;
|
||||
tree['f'] = {};
|
||||
tree[dupItems['adup2']] = {};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a', '0/0:b', '0/1:a', '0/0:d', '0/1:b', '0/0:f', '0/2:a']);
|
||||
return testObject.$getChildren('testNodeTreeProvider', '0/1:b')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/1:b/0:h', '0/1:b/1:h', '0/1:b/0:j', '0/1:b/1:j', '0/1:b/2:h']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('getChildren is not returned from cache if refreshed', (done) => {
|
||||
tree = {
|
||||
'c': {}
|
||||
};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:c']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('getChildren is returned from cache if not refreshed', () => {
|
||||
tree = {
|
||||
'c': {}
|
||||
};
|
||||
|
||||
return testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:a', '0/0:b']);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will throw an error if getParent is not implemented', () => {
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'a' })
|
||||
.then(() => assert.fail('Reveal should throw an error as getParent is not implemented'), () => null);
|
||||
});
|
||||
|
||||
test('reveal will return empty array for root element', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'a' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([], revealTarget.args[0][2]);
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will return parents array for an element when hierarchy is not loaded', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'aa' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will return parents array for an element when hierarchy is loaded', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return testObject.$getChildren('treeDataProvider')
|
||||
.then(() => testObject.$getChildren('treeDataProvider', '0/0:a'))
|
||||
.then(() => treeView.reveal({ key: 'aa' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
}));
|
||||
});
|
||||
|
||||
test('reveal will return parents array for deeper element with no selection', () => {
|
||||
tree = {
|
||||
'b': {
|
||||
'ba': {
|
||||
'bac': {}
|
||||
}
|
||||
}
|
||||
};
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'bac' }, { select: false, focus: false, expand: false })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:b/0:ba/0:bac', label: { label: 'bac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b/0:ba' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([
|
||||
{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed },
|
||||
{ handle: '0/0:b/0:ba', label: { label: 'ba' }, collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' }
|
||||
], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: false, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal after first udpate', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return loadCompleteTree('treeDataProvider')
|
||||
.then(() => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
|
||||
return treeView.reveal({ key: 'ac' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:ac', label: { label: 'ac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal after second udpate', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return loadCompleteTree('treeDataProvider')
|
||||
.then(() => {
|
||||
runWithEventMerging((resolve) => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bc': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
resolve();
|
||||
}).then(() => {
|
||||
return treeView.reveal({ key: 'bc' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function loadCompleteTree(treeId: string, element?: string): Promise<null> {
|
||||
return testObject.$getChildren(treeId, element)
|
||||
.then(elements => elements.map(e => loadCompleteTree(treeId, e.handle)))
|
||||
.then(() => null);
|
||||
}
|
||||
|
||||
function removeUnsetKeys(obj: any): any {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(o => removeUnsetKeys(o));
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const result: { [key: string]: any } = {};
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (obj[key] !== undefined) {
|
||||
result[key] = removeUnsetKeys(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function aNodeTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
return getTreeItem(element.key);
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNode.event
|
||||
};
|
||||
}
|
||||
|
||||
function aCompleteNodeTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
return getTreeItem(element.key);
|
||||
},
|
||||
getParent: ({ key }: { key: string }): { key: string } | undefined => {
|
||||
const parentKey = key.substring(0, key.length - 1);
|
||||
return parentKey ? new Key(parentKey) : undefined;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNode.event
|
||||
};
|
||||
}
|
||||
|
||||
function aNodeWithIdTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
const treeItem = getTreeItem(element.key);
|
||||
treeItem.id = element.key;
|
||||
return treeItem;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNodeWithId.event
|
||||
};
|
||||
}
|
||||
|
||||
function aNodeWithHighlightedLabelTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
const treeItem = getTreeItem(element.key, [[0, 2], [3, 5]]);
|
||||
treeItem.id = element.key;
|
||||
return treeItem;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNodeWithId.event
|
||||
};
|
||||
}
|
||||
|
||||
function getTreeElement(element: string): any {
|
||||
let parent = tree;
|
||||
for (let i = 0; i < element.length; i++) {
|
||||
parent = parent[element.substring(0, i + 1)];
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function getChildren(key: string | undefined): string[] {
|
||||
if (!key) {
|
||||
return Object.keys(tree);
|
||||
}
|
||||
let treeElement = getTreeElement(key);
|
||||
if (treeElement) {
|
||||
return Object.keys(treeElement);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getTreeItem(key: string, highlights?: [number, number][]): TreeItem {
|
||||
const treeElement = getTreeElement(key);
|
||||
return {
|
||||
label: <any>{ label: labels[key] || key, highlights },
|
||||
collapsibleState: treeElement && Object.keys(treeElement).length ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None
|
||||
};
|
||||
}
|
||||
|
||||
function getNode(key: string): { key: string } {
|
||||
if (!nodes[key]) {
|
||||
nodes[key] = new Key(key);
|
||||
}
|
||||
return nodes[key];
|
||||
}
|
||||
|
||||
class Key {
|
||||
constructor(readonly key: string) { }
|
||||
}
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user