Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/test/browser/parts/activitybar/activitybarPart.test.ts
13405 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 assert from 'assert';
7
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
9
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
10
import { TestThemeService } from '../../../../../platform/theme/test/common/testThemeService.js';
11
import { TestStorageService } from '../../../common/workbenchTestServices.js';
12
import { TestLayoutService } from '../../workbenchTestServices.js';
13
import { ActivitybarPart } from '../../../../browser/parts/activitybar/activitybarPart.js';
14
import { IViewSize } from '../../../../../base/browser/ui/grid/grid.js';
15
import { LayoutSettings, Parts } from '../../../../services/layout/browser/layoutService.js';
16
import { mainWindow } from '../../../../../base/browser/window.js';
17
import { IConfigurationChangeEvent } from '../../../../../platform/configuration/common/configuration.js';
18
import { IPaneCompositePart } from '../../../../browser/parts/paneCompositePart.js';
19
import { Event, Emitter } from '../../../../../base/common/event.js';
20
import { IPaneComposite } from '../../../../common/panecomposite.js';
21
import { Extensions, PaneCompositeDescriptor } from '../../../../browser/panecomposite.js';
22
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
23
import { ViewContainerLocation } from '../../../../common/views.js';
24
25
class StubPaneCompositePart implements IPaneCompositePart {
26
declare readonly _serviceBrand: undefined;
27
readonly partId = Parts.SIDEBAR_PART;
28
readonly registryId = Extensions.Viewlets;
29
element: HTMLElement = undefined!;
30
minimumWidth = 0;
31
maximumWidth = 0;
32
minimumHeight = 0;
33
maximumHeight = 0;
34
onDidChange = Event.None;
35
onDidPaneCompositeOpen = new Emitter<IPaneComposite>().event;
36
onDidPaneCompositeClose = new Emitter<IPaneComposite>().event;
37
openPaneComposite(): Promise<IPaneComposite | undefined> { return Promise.resolve(undefined); }
38
getPaneComposites(): PaneCompositeDescriptor[] { return []; }
39
getPaneComposite(): PaneCompositeDescriptor | undefined { return undefined; }
40
getActivePaneComposite(): IPaneComposite | undefined { return undefined; }
41
getProgressIndicator() { return undefined; }
42
hideActivePaneComposite(): void { }
43
getLastActivePaneCompositeId(): string { return ''; }
44
getPinnedPaneCompositeIds(): string[] { return []; }
45
getVisiblePaneCompositeIds(): string[] { return []; }
46
getPaneCompositeIds(): string[] { return []; }
47
layout(): void { }
48
dispose(): void { }
49
}
50
51
suite('ActivitybarPart', () => {
52
53
const disposables = new DisposableStore();
54
55
let fixture: HTMLElement;
56
const fixtureId = 'activitybar-part-fixture';
57
58
setup(() => {
59
fixture = document.createElement('div');
60
fixture.id = fixtureId;
61
mainWindow.document.body.appendChild(fixture);
62
});
63
64
teardown(() => {
65
fixture.remove();
66
disposables.clear();
67
});
68
69
function createActivitybarPart(compact: boolean): { part: ActivitybarPart; configService: TestConfigurationService } {
70
const configService = new TestConfigurationService({
71
[LayoutSettings.ACTIVITY_BAR_COMPACT]: compact,
72
});
73
const storageService = disposables.add(new TestStorageService());
74
const themeService = new TestThemeService();
75
const layoutService = new TestLayoutService();
76
77
// Override isVisible to return false so that create() does not call show()
78
// and attempt to instantiate the composite bar (which requires a full DI setup).
79
layoutService.isVisible = (_part: Parts) => false;
80
81
// Stub instantiation service—createCompositeBar is only called in show(),
82
// which we skip in unit tests focused on dimensions / style behaviour.
83
const stubInstantiationService = { createInstance: () => { throw new Error('not expected'); } } as unknown as IInstantiationService;
84
85
const part = disposables.add(new ActivitybarPart(
86
ViewContainerLocation.Sidebar,
87
new StubPaneCompositePart(),
88
stubInstantiationService,
89
layoutService,
90
themeService,
91
storageService,
92
configService,
93
));
94
95
return { part, configService };
96
}
97
98
function fireConfigChange(configService: TestConfigurationService, key: string): void {
99
configService.onDidChangeConfigurationEmitter.fire({
100
affectsConfiguration: (k: string) => k === key,
101
} satisfies Partial<IConfigurationChangeEvent> as unknown as IConfigurationChangeEvent);
102
}
103
104
// --- Static constants ---------------------------------------------------
105
106
test('default constants match original (pre-compact) dimensions', () => {
107
assert.deepStrictEqual(
108
{
109
width: ActivitybarPart.ACTIVITYBAR_WIDTH,
110
actionHeight: ActivitybarPart.ACTION_HEIGHT,
111
iconSize: ActivitybarPart.ICON_SIZE,
112
},
113
{
114
width: 48,
115
actionHeight: 48,
116
iconSize: 24,
117
}
118
);
119
});
120
121
test('compact constants match reduced dimensions', () => {
122
assert.deepStrictEqual(
123
{
124
width: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH,
125
actionHeight: ActivitybarPart.COMPACT_ACTION_HEIGHT,
126
iconSize: ActivitybarPart.COMPACT_ICON_SIZE,
127
},
128
{
129
width: 36,
130
actionHeight: 32,
131
iconSize: 16,
132
}
133
);
134
});
135
136
// --- Dimension getters --------------------------------------------------
137
138
test('default mode returns default width constraints', () => {
139
const { part } = createActivitybarPart(false);
140
assert.deepStrictEqual(
141
{ min: part.minimumWidth, max: part.maximumWidth },
142
{ min: ActivitybarPart.ACTIVITYBAR_WIDTH, max: ActivitybarPart.ACTIVITYBAR_WIDTH }
143
);
144
});
145
146
test('compact mode returns compact width constraints', () => {
147
const { part } = createActivitybarPart(true);
148
assert.deepStrictEqual(
149
{ min: part.minimumWidth, max: part.maximumWidth },
150
{ min: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH, max: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH }
151
);
152
});
153
154
test('height constraints are unbounded', () => {
155
const { part } = createActivitybarPart(false);
156
assert.strictEqual(part.minimumHeight, 0);
157
assert.strictEqual(part.maximumHeight, Number.POSITIVE_INFINITY);
158
});
159
160
// --- Configuration change: dimension update ----------------------------
161
162
test('toggling compact via config changes width constraints', () => {
163
const { part, configService } = createActivitybarPart(false);
164
165
// Initially default
166
assert.strictEqual(part.minimumWidth, ActivitybarPart.ACTIVITYBAR_WIDTH);
167
168
// Switch to compact
169
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);
170
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
171
172
assert.deepStrictEqual(
173
{ min: part.minimumWidth, max: part.maximumWidth },
174
{ min: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH, max: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH }
175
);
176
177
// Switch back to default
178
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);
179
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
180
181
assert.deepStrictEqual(
182
{ min: part.minimumWidth, max: part.maximumWidth },
183
{ min: ActivitybarPart.ACTIVITYBAR_WIDTH, max: ActivitybarPart.ACTIVITYBAR_WIDTH }
184
);
185
});
186
187
// --- onDidChange fires for grid ----------------------------------------
188
189
test('fires onDidChange(undefined) when compact setting changes', () => {
190
const { part, configService } = createActivitybarPart(false);
191
192
const events: (IViewSize | undefined)[] = [];
193
disposables.add(part.onDidChange(e => events.push(e)));
194
195
// Toggle to compact
196
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);
197
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
198
199
assert.strictEqual(events.length, 1);
200
assert.strictEqual(events[0], undefined, 'should fire undefined to signal constraint change');
201
202
// Toggle back
203
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);
204
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
205
206
assert.strictEqual(events.length, 2);
207
assert.strictEqual(events[1], undefined);
208
});
209
210
test('does not fire onDidChange for unrelated config changes', () => {
211
const { part, configService } = createActivitybarPart(false);
212
213
const events: (IViewSize | undefined)[] = [];
214
disposables.add(part.onDidChange(e => events.push(e)));
215
216
fireConfigChange(configService, 'editor.fontSize');
217
218
assert.strictEqual(events.length, 0);
219
});
220
221
// --- CSS custom properties on element -----------------------------------
222
223
test('updateCompactStyle sets correct CSS custom properties in default mode', () => {
224
const { part } = createActivitybarPart(false);
225
226
const el = document.createElement('div');
227
fixture.appendChild(el);
228
part.create(el);
229
230
assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);
231
assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.ACTION_HEIGHT}px`);
232
assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.ICON_SIZE}px`);
233
assert.strictEqual(el.classList.contains('compact'), false);
234
});
235
236
test('updateCompactStyle sets correct CSS custom properties in compact mode', () => {
237
const { part } = createActivitybarPart(true);
238
239
const el = document.createElement('div');
240
fixture.appendChild(el);
241
part.create(el);
242
243
assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH}px`);
244
assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.COMPACT_ACTION_HEIGHT}px`);
245
assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.COMPACT_ICON_SIZE}px`);
246
assert.strictEqual(el.classList.contains('compact'), true);
247
});
248
249
test('toggling compact updates CSS custom properties on element', () => {
250
const { part, configService } = createActivitybarPart(false);
251
252
const el = document.createElement('div');
253
fixture.appendChild(el);
254
part.create(el);
255
256
// Default state
257
assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);
258
assert.strictEqual(el.classList.contains('compact'), false);
259
260
// Switch to compact
261
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);
262
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
263
264
assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH}px`);
265
assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.COMPACT_ACTION_HEIGHT}px`);
266
assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.COMPACT_ICON_SIZE}px`);
267
assert.strictEqual(el.classList.contains('compact'), true);
268
269
// Switch back
270
configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);
271
fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);
272
273
assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);
274
assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.ACTION_HEIGHT}px`);
275
assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.ICON_SIZE}px`);
276
assert.strictEqual(el.classList.contains('compact'), false);
277
});
278
279
// --- toJSON ------------------------------------------------------------
280
281
test('toJSON returns correct part type', () => {
282
const { part } = createActivitybarPart(false);
283
assert.deepStrictEqual(part.toJSON(), { type: Parts.ACTIVITYBAR_PART });
284
});
285
286
ensureNoDisposablesAreLeakedInTestSuite();
287
});
288
289