Path: blob/main/src/vs/base/test/browser/ui/splitview/splitview.test.ts
3296 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 { Sash, SashState } from '../../../../browser/ui/sash/sash.js';7import { IView, LayoutPriority, Sizing, SplitView } from '../../../../browser/ui/splitview/splitview.js';8import { Emitter } from '../../../../common/event.js';9import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js';1011class TestView implements IView<number> {1213private readonly _onDidChange = new Emitter<number | undefined>();14readonly onDidChange = this._onDidChange.event;1516get minimumSize(): number { return this._minimumSize; }17set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(undefined); }1819get maximumSize(): number { return this._maximumSize; }20set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(undefined); }2122private _element: HTMLElement = document.createElement('div');23get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; }2425private readonly _onDidGetElement = new Emitter<void>();26readonly onDidGetElement = this._onDidGetElement.event;2728private _size = 0;29get size(): number { return this._size; }30private _orthogonalSize: number | undefined = 0;31get orthogonalSize(): number | undefined { return this._orthogonalSize; }32private readonly _onDidLayout = new Emitter<{ size: number; orthogonalSize: number | undefined }>();33readonly onDidLayout = this._onDidLayout.event;3435private readonly _onDidFocus = new Emitter<void>();36readonly onDidFocus = this._onDidFocus.event;3738constructor(39private _minimumSize: number,40private _maximumSize: number,41readonly priority: LayoutPriority = LayoutPriority.Normal42) {43assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size');44}4546layout(size: number, _offset: number, orthogonalSize: number | undefined): void {47this._size = size;48this._orthogonalSize = orthogonalSize;49this._onDidLayout.fire({ size, orthogonalSize });50}5152focus(): void {53this._onDidFocus.fire();54}5556dispose(): void {57this._onDidChange.dispose();58this._onDidGetElement.dispose();59this._onDidLayout.dispose();60this._onDidFocus.dispose();61}62}6364function getSashes(splitview: SplitView): Sash[] {65return splitview.sashItems.map((i: any) => i.sash) as Sash[];66}6768suite('Splitview', () => {6970const store = ensureNoDisposablesAreLeakedInTestSuite();7172let container: HTMLElement;7374setup(() => {75container = document.createElement('div');76container.style.position = 'absolute';77container.style.width = `${200}px`;78container.style.height = `${200}px`;79});8081test('empty splitview has empty DOM', () => {82store.add(new SplitView(container));83assert.strictEqual(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty');84});8586test('has views and sashes as children', () => {87const view1 = store.add(new TestView(20, 20));88const view2 = store.add(new TestView(20, 20));89const view3 = store.add(new TestView(20, 20));90const splitview = store.add(new SplitView(container));9192splitview.addView(view1, 20);93splitview.addView(view2, 20);94splitview.addView(view3, 20);9596let viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');97assert.strictEqual(viewQuery.length, 3, 'split view should have 3 views');9899let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');100assert.strictEqual(sashQuery.length, 2, 'split view should have 2 sashes');101102splitview.removeView(2);103104viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');105assert.strictEqual(viewQuery.length, 2, 'split view should have 2 views');106107sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');108assert.strictEqual(sashQuery.length, 1, 'split view should have 1 sash');109110splitview.removeView(0);111112viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');113assert.strictEqual(viewQuery.length, 1, 'split view should have 1 view');114115sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');116assert.strictEqual(sashQuery.length, 0, 'split view should have no sashes');117118splitview.removeView(0);119120viewQuery = container.querySelectorAll('.monaco-split-view2 > .monaco-scrollable-element > .split-view-container > .split-view-view');121assert.strictEqual(viewQuery.length, 0, 'split view should have no views');122123sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');124assert.strictEqual(sashQuery.length, 0, 'split view should have no sashes');125});126127test('calls view methods on addView and removeView', () => {128const view = store.add(new TestView(20, 20));129const splitview = store.add(new SplitView(container));130131let didLayout = false;132store.add(view.onDidLayout(() => didLayout = true));133store.add(view.onDidGetElement(() => undefined));134135splitview.addView(view, 20);136137assert.strictEqual(view.size, 20, 'view has right size');138assert(didLayout, 'layout is called');139assert(didLayout, 'render is called');140});141142test('stretches view to viewport', () => {143const view = store.add(new TestView(20, Number.POSITIVE_INFINITY));144const splitview = store.add(new SplitView(container));145splitview.layout(200);146147splitview.addView(view, 20);148assert.strictEqual(view.size, 200, 'view is stretched');149150splitview.layout(200);151assert.strictEqual(view.size, 200, 'view stayed the same');152153splitview.layout(100);154assert.strictEqual(view.size, 100, 'view is collapsed');155156splitview.layout(20);157assert.strictEqual(view.size, 20, 'view is collapsed');158159splitview.layout(10);160assert.strictEqual(view.size, 20, 'view is clamped');161162splitview.layout(200);163assert.strictEqual(view.size, 200, 'view is stretched');164});165166test('can resize views', () => {167const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));168const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));169const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));170const splitview = store.add(new SplitView(container));171splitview.layout(200);172173splitview.addView(view1, 20);174splitview.addView(view2, 20);175splitview.addView(view3, 20);176177assert.strictEqual(view1.size, 160, 'view1 is stretched');178assert.strictEqual(view2.size, 20, 'view2 size is 20');179assert.strictEqual(view3.size, 20, 'view3 size is 20');180181splitview.resizeView(1, 40);182183assert.strictEqual(view1.size, 140, 'view1 is collapsed');184assert.strictEqual(view2.size, 40, 'view2 is stretched');185assert.strictEqual(view3.size, 20, 'view3 stays the same');186187splitview.resizeView(0, 70);188189assert.strictEqual(view1.size, 70, 'view1 is collapsed');190assert.strictEqual(view2.size, 40, 'view2 stays the same');191assert.strictEqual(view3.size, 90, 'view3 is stretched');192193splitview.resizeView(2, 40);194195assert.strictEqual(view1.size, 70, 'view1 stays the same');196assert.strictEqual(view2.size, 90, 'view2 is collapsed');197assert.strictEqual(view3.size, 40, 'view3 is stretched');198});199200test('reacts to view changes', () => {201const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));202const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));203const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));204const splitview = store.add(new SplitView(container));205splitview.layout(200);206207splitview.addView(view1, 20);208splitview.addView(view2, 20);209splitview.addView(view3, 20);210211assert.strictEqual(view1.size, 160, 'view1 is stretched');212assert.strictEqual(view2.size, 20, 'view2 size is 20');213assert.strictEqual(view3.size, 20, 'view3 size is 20');214215view1.maximumSize = 20;216217assert.strictEqual(view1.size, 20, 'view1 is collapsed');218assert.strictEqual(view2.size, 20, 'view2 stays the same');219assert.strictEqual(view3.size, 160, 'view3 is stretched');220221view3.maximumSize = 40;222223assert.strictEqual(view1.size, 20, 'view1 stays the same');224assert.strictEqual(view2.size, 140, 'view2 is stretched');225assert.strictEqual(view3.size, 40, 'view3 is collapsed');226227view2.maximumSize = 200;228229assert.strictEqual(view1.size, 20, 'view1 stays the same');230assert.strictEqual(view2.size, 140, 'view2 stays the same');231assert.strictEqual(view3.size, 40, 'view3 stays the same');232233view3.maximumSize = Number.POSITIVE_INFINITY;234view3.minimumSize = 100;235236assert.strictEqual(view1.size, 20, 'view1 is collapsed');237assert.strictEqual(view2.size, 80, 'view2 is collapsed');238assert.strictEqual(view3.size, 100, 'view3 is stretched');239});240241test('sashes are properly enabled/disabled', () => {242const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));243const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));244const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));245const splitview = store.add(new SplitView(container));246splitview.layout(200);247248splitview.addView(view1, Sizing.Distribute);249splitview.addView(view2, Sizing.Distribute);250splitview.addView(view3, Sizing.Distribute);251252const sashes = getSashes(splitview);253assert.strictEqual(sashes.length, 2, 'there are two sashes');254assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');255assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');256257splitview.layout(60);258assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');259assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');260261splitview.layout(20);262assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');263assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');264265splitview.layout(200);266assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');267assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');268269view1.maximumSize = 20;270assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');271assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');272273view2.maximumSize = 20;274assert.strictEqual(sashes[0].state, SashState.Disabled, 'first sash is disabled');275assert.strictEqual(sashes[1].state, SashState.Disabled, 'second sash is disabled');276277view1.maximumSize = 300;278assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');279assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');280281view2.maximumSize = 200;282assert.strictEqual(sashes[0].state, SashState.AtMinimum, 'first sash is enabled');283assert.strictEqual(sashes[1].state, SashState.AtMinimum, 'second sash is enabled');284285splitview.resizeView(0, 40);286assert.strictEqual(sashes[0].state, SashState.Enabled, 'first sash is enabled');287assert.strictEqual(sashes[1].state, SashState.Enabled, 'second sash is enabled');288});289290test('issue #35497', () => {291const view1 = store.add(new TestView(160, Number.POSITIVE_INFINITY));292const view2 = store.add(new TestView(66, 66));293294const splitview = store.add(new SplitView(container));295splitview.layout(986);296297splitview.addView(view1, 142, 0);298assert.strictEqual(view1.size, 986, 'first view is stretched');299300store.add(view2.onDidGetElement(() => {301assert.throws(() => splitview.resizeView(1, 922));302assert.throws(() => splitview.resizeView(1, 922));303}));304305splitview.addView(view2, 66, 0);306assert.strictEqual(view2.size, 66, 'second view is fixed');307assert.strictEqual(view1.size, 986 - 66, 'first view is collapsed');308309const viewContainers = container.querySelectorAll('.split-view-view');310assert.strictEqual(viewContainers.length, 2, 'there are two view containers');311assert.strictEqual((viewContainers.item(0) as HTMLElement).style.height, '66px', 'second view container is 66px');312assert.strictEqual<string>((viewContainers.item(1) as HTMLElement).style.height, `${986 - 66}px`, 'first view container is 66px');313});314315test('automatic size distribution', () => {316const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));317const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));318const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));319const splitview = store.add(new SplitView(container));320splitview.layout(200);321322splitview.addView(view1, Sizing.Distribute);323assert.strictEqual(view1.size, 200);324325splitview.addView(view2, 50);326assert.deepStrictEqual([view1.size, view2.size], [150, 50]);327328splitview.addView(view3, Sizing.Distribute);329assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 66, 68]);330331splitview.removeView(1, Sizing.Distribute);332assert.deepStrictEqual([view1.size, view3.size], [100, 100]);333});334335test('add views before layout', () => {336const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));337const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));338const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));339const splitview = store.add(new SplitView(container));340341splitview.addView(view1, 100);342splitview.addView(view2, 75);343splitview.addView(view3, 25);344345splitview.layout(200);346assert.deepStrictEqual([view1.size, view2.size, view3.size], [67, 67, 66]);347});348349test('split sizing', () => {350const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));351const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));352const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));353const splitview = store.add(new SplitView(container));354splitview.layout(200);355356splitview.addView(view1, Sizing.Distribute);357assert.strictEqual(view1.size, 200);358359splitview.addView(view2, Sizing.Split(0));360assert.deepStrictEqual([view1.size, view2.size], [100, 100]);361362splitview.addView(view3, Sizing.Split(1));363assert.deepStrictEqual([view1.size, view2.size, view3.size], [100, 50, 50]);364});365366test('split sizing 2', () => {367const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));368const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));369const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));370const splitview = store.add(new SplitView(container));371splitview.layout(200);372373splitview.addView(view1, Sizing.Distribute);374assert.strictEqual(view1.size, 200);375376splitview.addView(view2, Sizing.Split(0));377assert.deepStrictEqual([view1.size, view2.size], [100, 100]);378379splitview.addView(view3, Sizing.Split(0));380assert.deepStrictEqual([view1.size, view2.size, view3.size], [50, 100, 50]);381});382383test('proportional layout', () => {384const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));385const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));386const splitview = store.add(new SplitView(container));387splitview.layout(200);388389splitview.addView(view1, Sizing.Distribute);390splitview.addView(view2, Sizing.Distribute);391assert.deepStrictEqual([view1.size, view2.size], [100, 100]);392393splitview.layout(100);394assert.deepStrictEqual([view1.size, view2.size], [50, 50]);395});396397test('disable proportional layout', () => {398const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));399const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));400const splitview = store.add(new SplitView(container, { proportionalLayout: false }));401splitview.layout(200);402403splitview.addView(view1, Sizing.Distribute);404splitview.addView(view2, Sizing.Distribute);405assert.deepStrictEqual([view1.size, view2.size], [100, 100]);406407splitview.layout(100);408assert.deepStrictEqual([view1.size, view2.size], [80, 20]);409});410411test('high layout priority', () => {412const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));413const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.High));414const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY));415const splitview = store.add(new SplitView(container, { proportionalLayout: false }));416splitview.layout(200);417418splitview.addView(view1, Sizing.Distribute);419splitview.addView(view2, Sizing.Distribute);420splitview.addView(view3, Sizing.Distribute);421assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 68, 66]);422423splitview.layout(180);424assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 48, 66]);425426splitview.layout(124);427assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 20, 38]);428429splitview.layout(60);430assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 20, 20]);431432splitview.layout(200);433assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 160, 20]);434});435436test('low layout priority', () => {437const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));438const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));439const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low));440const splitview = store.add(new SplitView(container, { proportionalLayout: false }));441splitview.layout(200);442443splitview.addView(view1, Sizing.Distribute);444splitview.addView(view2, Sizing.Distribute);445splitview.addView(view3, Sizing.Distribute);446assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 68, 66]);447448splitview.layout(180);449assert.deepStrictEqual([view1.size, view2.size, view3.size], [66, 48, 66]);450451splitview.layout(132);452assert.deepStrictEqual([view1.size, view2.size, view3.size], [46, 20, 66]);453454splitview.layout(60);455assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 20, 20]);456457splitview.layout(200);458assert.deepStrictEqual([view1.size, view2.size, view3.size], [20, 160, 20]);459});460461test('context propagates to views', () => {462const view1 = store.add(new TestView(20, Number.POSITIVE_INFINITY));463const view2 = store.add(new TestView(20, Number.POSITIVE_INFINITY));464const view3 = store.add(new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low));465const splitview = store.add(new SplitView<number>(container, { proportionalLayout: false }));466splitview.layout(200);467468splitview.addView(view1, Sizing.Distribute);469splitview.addView(view2, Sizing.Distribute);470splitview.addView(view3, Sizing.Distribute);471472splitview.layout(200, 100);473assert.deepStrictEqual([view1.orthogonalSize, view2.orthogonalSize, view3.orthogonalSize], [100, 100, 100]);474});475});476477478