Path: blob/main/src/vs/platform/contextkey/test/browser/contextkey.test.ts
5240 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 { DeferredPromise } from '../../../../base/common/async.js';7import { URI } from '../../../../base/common/uri.js';8import { mock } from '../../../../base/test/common/mock.js';9import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';10import { IConfigurationService } from '../../../configuration/common/configuration.js';11import { TestConfigurationService } from '../../../configuration/test/common/testConfigurationService.js';12import { ContextKeyService, setContext } from '../../browser/contextKeyService.js';13import { ContextKeyExpr, IContextKeyService } from '../../common/contextkey.js';14import { ServiceCollection } from '../../../instantiation/common/serviceCollection.js';15import { TestInstantiationService } from '../../../instantiation/test/common/instantiationServiceMock.js';16import { ITelemetryService } from '../../../telemetry/common/telemetry.js';1718suite('ContextKeyService', () => {19const testDisposables = ensureNoDisposablesAreLeakedInTestSuite();2021test('updateParent', () => {22const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));23const parent1 = testDisposables.add(root.createScoped(document.createElement('div')));24const parent2 = testDisposables.add(root.createScoped(document.createElement('div')));2526const child = testDisposables.add(parent1.createScoped(document.createElement('div')));27parent1.createKey('testA', 1);28parent1.createKey('testB', 2);29parent1.createKey('testD', 0);3031parent2.createKey('testA', 3);32parent2.createKey('testC', 4);33parent2.createKey('testD', 0);3435let complete: () => void;36let reject: (err: Error) => void;37const p = new Promise<void>((_complete, _reject) => {38complete = _complete;39reject = _reject;40});41testDisposables.add(child.onDidChangeContext(e => {42try {43assert.ok(e.affectsSome(new Set(['testA'])), 'testA changed');44assert.ok(e.affectsSome(new Set(['testB'])), 'testB changed');45assert.ok(e.affectsSome(new Set(['testC'])), 'testC changed');46assert.ok(!e.affectsSome(new Set(['testD'])), 'testD did not change');4748assert.strictEqual(child.getContextKeyValue('testA'), 3);49assert.strictEqual(child.getContextKeyValue('testB'), undefined);50assert.strictEqual(child.getContextKeyValue('testC'), 4);51assert.strictEqual(child.getContextKeyValue('testD'), 0);52} catch (err) {53reject(err);54return;55}5657complete();58}));5960child.updateParent(parent2);6162return p;63});6465test('updateParent to same service', () => {66const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));67const parent1 = testDisposables.add(root.createScoped(document.createElement('div')));6869const child = testDisposables.add(parent1.createScoped(document.createElement('div')));70parent1.createKey('testA', 1);71parent1.createKey('testB', 2);72parent1.createKey('testD', 0);7374let eventFired = false;75testDisposables.add(child.onDidChangeContext(e => {76eventFired = true;77}));7879child.updateParent(parent1);8081assert.strictEqual(eventFired, false);82});8384test('issue #147732: URIs as context values', () => {85const configurationService: IConfigurationService = new TestConfigurationService();86const contextKeyService: IContextKeyService = testDisposables.add(new ContextKeyService(configurationService));87const instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection(88[IConfigurationService, configurationService],89[IContextKeyService, contextKeyService],90[ITelemetryService, new class extends mock<ITelemetryService>() {91override async publicLog2() {92//93}94}]95)));9697const uri = URI.parse('test://abc');98contextKeyService.createKey<string>('notebookCellResource', undefined).set(uri.toString());99instantiationService.invokeFunction(setContext, 'jupyter.runByLineCells', JSON.parse(JSON.stringify([uri])));100101const expr = ContextKeyExpr.in('notebookCellResource', 'jupyter.runByLineCells');102assert.deepStrictEqual(contextKeyService.contextMatchesRules(expr), true);103});104105test('suppress update event from parent when one key is overridden by child', () => {106const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));107const child = testDisposables.add(root.createScoped(document.createElement('div')));108109root.createKey('testA', 1);110child.createKey('testA', 4);111112let fired = false;113const event = testDisposables.add(child.onDidChangeContext(e => fired = true));114root.setContext('testA', 10);115assert.strictEqual(fired, false, 'Should not fire event when overridden key is updated in parent');116event.dispose();117});118119test('suppress update event from parent when all keys are overridden by child', () => {120const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));121const child = testDisposables.add(root.createScoped(document.createElement('div')));122123root.createKey('testA', 1);124root.createKey('testB', 2);125root.createKey('testC', 3);126127child.createKey('testA', 4);128child.createKey('testB', 5);129child.createKey('testD', 6);130131let fired = false;132const event = testDisposables.add(child.onDidChangeContext(e => fired = true));133root.bufferChangeEvents(() => {134root.setContext('testA', 10);135root.setContext('testB', 20);136root.setContext('testD', 30);137});138139assert.strictEqual(fired, false, 'Should not fire event when overridden key is updated in parent');140event.dispose();141});142143test('pass through update event from parent when one key is not overridden by child', () => {144const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));145const child = testDisposables.add(root.createScoped(document.createElement('div')));146147root.createKey('testA', 1);148root.createKey('testB', 2);149root.createKey('testC', 3);150151child.createKey('testA', 4);152child.createKey('testB', 5);153child.createKey('testD', 6);154155const def = new DeferredPromise();156testDisposables.add(child.onDidChangeContext(e => {157try {158assert.ok(e.affectsSome(new Set(['testA'])), 'testA changed');159assert.ok(e.affectsSome(new Set(['testB'])), 'testB changed');160assert.ok(e.affectsSome(new Set(['testC'])), 'testC changed');161} catch (err) {162def.error(err);163return;164}165166def.complete(undefined);167}));168169root.bufferChangeEvents(() => {170root.setContext('testA', 10);171root.setContext('testB', 20);172root.setContext('testC', 30);173});174175return def.p;176});177178test('setting identical array values should not fire change event', () => {179const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));180const key = root.createKey<string[]>('testArray', ['a', 'b', 'c']);181182let eventFired = false;183testDisposables.add(root.onDidChangeContext(e => {184eventFired = true;185}));186187// Set the same array content (different reference)188key.set(['a', 'b', 'c']);189190assert.strictEqual(eventFired, false, 'Should not fire event when setting identical array');191});192193test('setting different array values should fire change event', () => {194const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));195const key = root.createKey<string[]>('testArray', ['a', 'b', 'c']);196197let eventFired = false;198testDisposables.add(root.onDidChangeContext(e => {199eventFired = true;200}));201202// Set a different array203key.set(['a', 'b', 'd']);204205assert.strictEqual(eventFired, true, 'Should fire event when setting different array');206});207208test('setting identical complex object should not fire change event', () => {209const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));210const initialValue = { foo: 'bar', count: 42 };211const key = root.createKey<Record<string, string | number>>('testObject', initialValue);212213let eventFired = false;214testDisposables.add(root.onDidChangeContext(e => {215eventFired = true;216}));217218// Set the same object content (different reference)219key.set({ foo: 'bar', count: 42 });220221assert.strictEqual(eventFired, false, 'Should not fire event when setting identical object');222});223224test('setting different complex object should fire change event', () => {225const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));226const initialValue = { foo: 'bar', count: 42 };227const key = root.createKey<Record<string, string | number>>('testObject', initialValue);228229let eventFired = false;230testDisposables.add(root.onDidChangeContext(e => {231eventFired = true;232}));233234// Set a different object235key.set({ foo: 'bar', count: 43 });236237assert.strictEqual(eventFired, true, 'Should fire event when setting different object');238});239240test('setting empty arrays should not fire change event when identical', () => {241const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));242const key = root.createKey<string[]>('testArray', []);243244let eventFired = false;245testDisposables.add(root.onDidChangeContext(e => {246eventFired = true;247}));248249// Set another empty array250key.set([]);251252assert.strictEqual(eventFired, false, 'Should not fire event when setting identical empty array');253});254255test('setting nested arrays should handle deep equality', () => {256const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));257const initialValue = ['a:b', 'c:d'];258const key = root.createKey<string[]>('testComplexArray', initialValue);259260let eventFired = false;261testDisposables.add(root.onDidChangeContext(e => {262eventFired = true;263}));264265// Set the same array content with colon-separated values266key.set(['a:b', 'c:d']);267268assert.strictEqual(eventFired, false, 'Should not fire event when setting identical array with complex values');269});270271test('setting same primitive values should not fire change event', () => {272const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));273const key = root.createKey('testString', 'hello');274275let eventFired = false;276testDisposables.add(root.onDidChangeContext(e => {277eventFired = true;278}));279280// Set the same string value281key.set('hello');282283assert.strictEqual(eventFired, false, 'Should not fire event when setting identical string');284});285286test('setting different primitive values should fire change event', () => {287const root = testDisposables.add(new ContextKeyService(new TestConfigurationService()));288const key = root.createKey<number>('testNumber', 42);289290let eventFired = false;291testDisposables.add(root.onDidChangeContext(e => {292eventFired = true;293}));294295// Set a different number value296key.set(43);297298assert.strictEqual(eventFired, true, 'Should fire event when setting different number');299});300});301302303