Path: blob/main/src/vs/workbench/test/browser/parts/activitybar/activitybarPart.test.ts
13405 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { DisposableStore } from '../../../../../base/common/lifecycle.js';7import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';8import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';9import { TestThemeService } from '../../../../../platform/theme/test/common/testThemeService.js';10import { TestStorageService } from '../../../common/workbenchTestServices.js';11import { TestLayoutService } from '../../workbenchTestServices.js';12import { ActivitybarPart } from '../../../../browser/parts/activitybar/activitybarPart.js';13import { IViewSize } from '../../../../../base/browser/ui/grid/grid.js';14import { LayoutSettings, Parts } from '../../../../services/layout/browser/layoutService.js';15import { mainWindow } from '../../../../../base/browser/window.js';16import { IConfigurationChangeEvent } from '../../../../../platform/configuration/common/configuration.js';17import { IPaneCompositePart } from '../../../../browser/parts/paneCompositePart.js';18import { Event, Emitter } from '../../../../../base/common/event.js';19import { IPaneComposite } from '../../../../common/panecomposite.js';20import { Extensions, PaneCompositeDescriptor } from '../../../../browser/panecomposite.js';21import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';22import { ViewContainerLocation } from '../../../../common/views.js';2324class StubPaneCompositePart implements IPaneCompositePart {25declare readonly _serviceBrand: undefined;26readonly partId = Parts.SIDEBAR_PART;27readonly registryId = Extensions.Viewlets;28element: HTMLElement = undefined!;29minimumWidth = 0;30maximumWidth = 0;31minimumHeight = 0;32maximumHeight = 0;33onDidChange = Event.None;34onDidPaneCompositeOpen = new Emitter<IPaneComposite>().event;35onDidPaneCompositeClose = new Emitter<IPaneComposite>().event;36openPaneComposite(): Promise<IPaneComposite | undefined> { return Promise.resolve(undefined); }37getPaneComposites(): PaneCompositeDescriptor[] { return []; }38getPaneComposite(): PaneCompositeDescriptor | undefined { return undefined; }39getActivePaneComposite(): IPaneComposite | undefined { return undefined; }40getProgressIndicator() { return undefined; }41hideActivePaneComposite(): void { }42getLastActivePaneCompositeId(): string { return ''; }43getPinnedPaneCompositeIds(): string[] { return []; }44getVisiblePaneCompositeIds(): string[] { return []; }45getPaneCompositeIds(): string[] { return []; }46layout(): void { }47dispose(): void { }48}4950suite('ActivitybarPart', () => {5152const disposables = new DisposableStore();5354let fixture: HTMLElement;55const fixtureId = 'activitybar-part-fixture';5657setup(() => {58fixture = document.createElement('div');59fixture.id = fixtureId;60mainWindow.document.body.appendChild(fixture);61});6263teardown(() => {64fixture.remove();65disposables.clear();66});6768function createActivitybarPart(compact: boolean): { part: ActivitybarPart; configService: TestConfigurationService } {69const configService = new TestConfigurationService({70[LayoutSettings.ACTIVITY_BAR_COMPACT]: compact,71});72const storageService = disposables.add(new TestStorageService());73const themeService = new TestThemeService();74const layoutService = new TestLayoutService();7576// Override isVisible to return false so that create() does not call show()77// and attempt to instantiate the composite bar (which requires a full DI setup).78layoutService.isVisible = (_part: Parts) => false;7980// Stub instantiation service—createCompositeBar is only called in show(),81// which we skip in unit tests focused on dimensions / style behaviour.82const stubInstantiationService = { createInstance: () => { throw new Error('not expected'); } } as unknown as IInstantiationService;8384const part = disposables.add(new ActivitybarPart(85ViewContainerLocation.Sidebar,86new StubPaneCompositePart(),87stubInstantiationService,88layoutService,89themeService,90storageService,91configService,92));9394return { part, configService };95}9697function fireConfigChange(configService: TestConfigurationService, key: string): void {98configService.onDidChangeConfigurationEmitter.fire({99affectsConfiguration: (k: string) => k === key,100} satisfies Partial<IConfigurationChangeEvent> as unknown as IConfigurationChangeEvent);101}102103// --- Static constants ---------------------------------------------------104105test('default constants match original (pre-compact) dimensions', () => {106assert.deepStrictEqual(107{108width: ActivitybarPart.ACTIVITYBAR_WIDTH,109actionHeight: ActivitybarPart.ACTION_HEIGHT,110iconSize: ActivitybarPart.ICON_SIZE,111},112{113width: 48,114actionHeight: 48,115iconSize: 24,116}117);118});119120test('compact constants match reduced dimensions', () => {121assert.deepStrictEqual(122{123width: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH,124actionHeight: ActivitybarPart.COMPACT_ACTION_HEIGHT,125iconSize: ActivitybarPart.COMPACT_ICON_SIZE,126},127{128width: 36,129actionHeight: 32,130iconSize: 16,131}132);133});134135// --- Dimension getters --------------------------------------------------136137test('default mode returns default width constraints', () => {138const { part } = createActivitybarPart(false);139assert.deepStrictEqual(140{ min: part.minimumWidth, max: part.maximumWidth },141{ min: ActivitybarPart.ACTIVITYBAR_WIDTH, max: ActivitybarPart.ACTIVITYBAR_WIDTH }142);143});144145test('compact mode returns compact width constraints', () => {146const { part } = createActivitybarPart(true);147assert.deepStrictEqual(148{ min: part.minimumWidth, max: part.maximumWidth },149{ min: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH, max: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH }150);151});152153test('height constraints are unbounded', () => {154const { part } = createActivitybarPart(false);155assert.strictEqual(part.minimumHeight, 0);156assert.strictEqual(part.maximumHeight, Number.POSITIVE_INFINITY);157});158159// --- Configuration change: dimension update ----------------------------160161test('toggling compact via config changes width constraints', () => {162const { part, configService } = createActivitybarPart(false);163164// Initially default165assert.strictEqual(part.minimumWidth, ActivitybarPart.ACTIVITYBAR_WIDTH);166167// Switch to compact168configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);169fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);170171assert.deepStrictEqual(172{ min: part.minimumWidth, max: part.maximumWidth },173{ min: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH, max: ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH }174);175176// Switch back to default177configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);178fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);179180assert.deepStrictEqual(181{ min: part.minimumWidth, max: part.maximumWidth },182{ min: ActivitybarPart.ACTIVITYBAR_WIDTH, max: ActivitybarPart.ACTIVITYBAR_WIDTH }183);184});185186// --- onDidChange fires for grid ----------------------------------------187188test('fires onDidChange(undefined) when compact setting changes', () => {189const { part, configService } = createActivitybarPart(false);190191const events: (IViewSize | undefined)[] = [];192disposables.add(part.onDidChange(e => events.push(e)));193194// Toggle to compact195configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);196fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);197198assert.strictEqual(events.length, 1);199assert.strictEqual(events[0], undefined, 'should fire undefined to signal constraint change');200201// Toggle back202configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);203fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);204205assert.strictEqual(events.length, 2);206assert.strictEqual(events[1], undefined);207});208209test('does not fire onDidChange for unrelated config changes', () => {210const { part, configService } = createActivitybarPart(false);211212const events: (IViewSize | undefined)[] = [];213disposables.add(part.onDidChange(e => events.push(e)));214215fireConfigChange(configService, 'editor.fontSize');216217assert.strictEqual(events.length, 0);218});219220// --- CSS custom properties on element -----------------------------------221222test('updateCompactStyle sets correct CSS custom properties in default mode', () => {223const { part } = createActivitybarPart(false);224225const el = document.createElement('div');226fixture.appendChild(el);227part.create(el);228229assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);230assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.ACTION_HEIGHT}px`);231assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.ICON_SIZE}px`);232assert.strictEqual(el.classList.contains('compact'), false);233});234235test('updateCompactStyle sets correct CSS custom properties in compact mode', () => {236const { part } = createActivitybarPart(true);237238const el = document.createElement('div');239fixture.appendChild(el);240part.create(el);241242assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH}px`);243assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.COMPACT_ACTION_HEIGHT}px`);244assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.COMPACT_ICON_SIZE}px`);245assert.strictEqual(el.classList.contains('compact'), true);246});247248test('toggling compact updates CSS custom properties on element', () => {249const { part, configService } = createActivitybarPart(false);250251const el = document.createElement('div');252fixture.appendChild(el);253part.create(el);254255// Default state256assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);257assert.strictEqual(el.classList.contains('compact'), false);258259// Switch to compact260configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, true);261fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);262263assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.COMPACT_ACTIVITYBAR_WIDTH}px`);264assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.COMPACT_ACTION_HEIGHT}px`);265assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.COMPACT_ICON_SIZE}px`);266assert.strictEqual(el.classList.contains('compact'), true);267268// Switch back269configService.setUserConfiguration(LayoutSettings.ACTIVITY_BAR_COMPACT, false);270fireConfigChange(configService, LayoutSettings.ACTIVITY_BAR_COMPACT);271272assert.strictEqual(el.style.getPropertyValue('--activity-bar-width'), `${ActivitybarPart.ACTIVITYBAR_WIDTH}px`);273assert.strictEqual(el.style.getPropertyValue('--activity-bar-action-height'), `${ActivitybarPart.ACTION_HEIGHT}px`);274assert.strictEqual(el.style.getPropertyValue('--activity-bar-icon-size'), `${ActivitybarPart.ICON_SIZE}px`);275assert.strictEqual(el.classList.contains('compact'), false);276});277278// --- toJSON ------------------------------------------------------------279280test('toJSON returns correct part type', () => {281const { part } = createActivitybarPart(false);282assert.deepStrictEqual(part.toJSON(), { type: Parts.ACTIVITYBAR_PART });283});284285ensureNoDisposablesAreLeakedInTestSuite();286});287288289