Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts
5222 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as nls from '../../../../nls.js';
7
import assert from 'assert';
8
import { mock } from '../../../../base/test/common/mock.js';
9
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
10
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
11
import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js';
12
import { NullLogService } from '../../../../platform/log/common/log.js';
13
import { TestNotificationService } from '../../../../platform/notification/test/common/testNotificationService.js';
14
import { Registry } from '../../../../platform/registry/common/platform.js';
15
import { NullTelemetryService } from '../../../../platform/telemetry/common/telemetryUtils.js';
16
import { MainThreadTreeViews } from '../../browser/mainThreadTreeViews.js';
17
import { DataTransferDTO, ExtHostTreeViewsShape } from '../../common/extHost.protocol.js';
18
import { CustomTreeView } from '../../../browser/parts/views/treeView.js';
19
import { Extensions, ITreeItem, ITreeView, ITreeViewDescriptor, IViewContainersRegistry, IViewDescriptorService, IViewsRegistry, TreeItemCollapsibleState, ViewContainer, ViewContainerLocation } from '../../../common/views.js';
20
import { IExtHostContext } from '../../../services/extensions/common/extHostCustomers.js';
21
import { ExtensionHostKind } from '../../../services/extensions/common/extensionHostKind.js';
22
import { ViewDescriptorService } from '../../../services/views/browser/viewDescriptorService.js';
23
import { TestViewsService, workbenchInstantiationService } from '../../../test/browser/workbenchTestServices.js';
24
import { TestExtensionService } from '../../../test/common/workbenchTestServices.js';
25
import { CancellationToken } from '../../../../base/common/cancellation.js';
26
import { Mimes } from '../../../../base/common/mime.js';
27
import { URI } from '../../../../base/common/uri.js';
28
29
suite('MainThreadHostTreeView', function () {
30
const testTreeViewId = 'testTreeView';
31
const customValue = 'customValue';
32
const ViewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
33
34
interface CustomTreeItem extends ITreeItem {
35
customProp: string;
36
}
37
38
class MockExtHostTreeViewsShape extends mock<ExtHostTreeViewsShape>() {
39
override async $getChildren(treeViewId: string, treeItemHandle?: string[]): Promise<(number | ITreeItem)[][]> {
40
return [[0, <CustomTreeItem>{ handle: 'testItem1', collapsibleState: TreeItemCollapsibleState.Expanded, customProp: customValue }]];
41
}
42
43
override async $hasResolve(): Promise<boolean> {
44
return false;
45
}
46
47
override $setVisible(): void { }
48
}
49
50
let container: ViewContainer;
51
let mainThreadTreeViews: MainThreadTreeViews;
52
let extHostTreeViewsShape: MockExtHostTreeViewsShape;
53
let instantiationService: TestInstantiationService;
54
55
teardown(() => {
56
ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container);
57
});
58
59
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
60
61
setup(async () => {
62
instantiationService = workbenchInstantiationService(undefined, disposables);
63
const viewDescriptorService = disposables.add(instantiationService.createInstance(ViewDescriptorService));
64
instantiationService.stub(IViewDescriptorService, viewDescriptorService);
65
// eslint-disable-next-line local/code-no-any-casts
66
container = Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', title: nls.localize2('test', 'test'), ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
67
const viewDescriptor: ITreeViewDescriptor = {
68
id: testTreeViewId,
69
ctorDescriptor: null!,
70
name: nls.localize2('Test View 1', 'Test View 1'),
71
treeView: disposables.add(instantiationService.createInstance(CustomTreeView, 'testTree', 'Test Title', 'extension.id')),
72
};
73
ViewsRegistry.registerViews([viewDescriptor], container);
74
75
const testExtensionService = new TestExtensionService();
76
extHostTreeViewsShape = new MockExtHostTreeViewsShape();
77
mainThreadTreeViews = disposables.add(new MainThreadTreeViews(
78
new class implements IExtHostContext {
79
remoteAuthority = '';
80
extensionHostKind = ExtensionHostKind.LocalProcess;
81
dispose() { }
82
assertRegistered() { }
83
set(v: any): any { return null; }
84
getProxy(): any {
85
return extHostTreeViewsShape;
86
}
87
drain(): any { return null; }
88
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService(), NullTelemetryService));
89
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, manuallyManageCheckboxes: false });
90
await testExtensionService.whenInstalledExtensionsRegistered();
91
});
92
93
test('getChildren keeps custom properties', async () => {
94
const treeView: ITreeView = (<ITreeViewDescriptor>ViewsRegistry.getView(testTreeViewId)).treeView;
95
const children = await treeView.dataProvider?.getChildren({ handle: 'root', collapsibleState: TreeItemCollapsibleState.Expanded });
96
assert(children!.length === 1, 'Exactly one child should be returned');
97
assert((<CustomTreeItem>children![0]).customProp === customValue, 'Tree Items should keep custom properties');
98
});
99
100
test('handleDrag reconstructs URI list from uriListData', async () => {
101
const testTreeViewIdWithDrag = 'testTreeViewWithDrag';
102
103
// Create a mock that returns URI list data
104
const mockExtHostWithDrag = new class extends mock<ExtHostTreeViewsShape>() {
105
override async $getChildren(treeViewId: string, treeItemHandle?: string[]): Promise<(number | ITreeItem)[][]> {
106
return [[0, { handle: 'item1', collapsibleState: TreeItemCollapsibleState.None }]];
107
}
108
109
override async $hasResolve(): Promise<boolean> {
110
return false;
111
}
112
113
override $setVisible(): void { }
114
115
override async $handleDrag(_sourceViewId: string, _sourceTreeItemHandles: string[], _operationUuid: string, _token: CancellationToken): Promise<DataTransferDTO | undefined> {
116
// Return a DataTransferDTO with text/uri-list containing uriListData
117
// This simulates what the extension host sends after URI transformation
118
return {
119
items: [
120
[Mimes.uriList, {
121
id: 'test-id',
122
// This is the original (untransformed) string - should NOT be used
123
asString: 'file:///original/untransformed/path.txt',
124
fileData: undefined,
125
// This is the transformed URI data - should be used
126
uriListData: [
127
{ scheme: 'file', authority: '', path: '/transformed/correct/path.txt', query: '', fragment: '' }
128
]
129
}]
130
]
131
};
132
}
133
}();
134
135
// Register a view with drag support
136
const viewDescriptorWithDrag: ITreeViewDescriptor = {
137
id: testTreeViewIdWithDrag,
138
ctorDescriptor: null!,
139
name: nls.localize2('Test View 2', 'Test View 2'),
140
treeView: disposables.add(instantiationService.createInstance(CustomTreeView, 'testTree2', 'Test Title 2', 'extension.id')),
141
};
142
ViewsRegistry.registerViews([viewDescriptorWithDrag], container);
143
144
const dragTestExtensionService = new TestExtensionService();
145
const dragTestMainThreadTreeViews = disposables.add(new MainThreadTreeViews(
146
new class implements IExtHostContext {
147
remoteAuthority = '';
148
extensionHostKind = ExtensionHostKind.LocalProcess;
149
dispose() { }
150
assertRegistered() { }
151
set(v: any): any { return null; }
152
getProxy(): any {
153
return mockExtHostWithDrag;
154
}
155
drain(): any { return null; }
156
}, new TestViewsService(), new TestNotificationService(), dragTestExtensionService, new NullLogService(), NullTelemetryService));
157
dragTestMainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewIdWithDrag, {
158
showCollapseAll: false,
159
canSelectMany: false,
160
dropMimeTypes: [],
161
dragMimeTypes: [Mimes.uriList],
162
hasHandleDrag: true,
163
hasHandleDrop: false,
164
manuallyManageCheckboxes: false
165
});
166
await dragTestExtensionService.whenInstalledExtensionsRegistered();
167
168
// Get the tree view and its drag controller
169
const dragTestTreeView: ITreeView = (<ITreeViewDescriptor>ViewsRegistry.getView(testTreeViewIdWithDrag)).treeView;
170
const dragController = dragTestTreeView.dragAndDropController;
171
assert(dragController, 'Drag controller should exist');
172
173
// Call handleDrag
174
const result = await dragController.handleDrag(['item1'], 'test-operation-uuid', CancellationToken.None);
175
assert(result, 'Result should not be undefined');
176
177
// Verify that the URI list was reconstructed from uriListData, not asString
178
const uriListItem = result.get(Mimes.uriList);
179
assert(uriListItem, 'URI list item should exist');
180
181
const uriListValue = await uriListItem.asString();
182
// The value should be the transformed URI, not the original untransformed one
183
assert.strictEqual(uriListValue, URI.from({ scheme: 'file', authority: '', path: '/transformed/correct/path.txt', query: '', fragment: '' }).toString());
184
});
185
186
187
});
188
189