Path: blob/main/src/vs/platform/keybinding/test/common/abstractKeybindingService.test.ts
5237 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*--------------------------------------------------------------------------------------------*/4import assert from 'assert';5import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';6import { createSimpleKeybinding, ResolvedKeybinding, KeyCodeChord, Keybinding } from '../../../../base/common/keybindings.js';7import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';8import { OS } from '../../../../base/common/platform.js';9import Severity from '../../../../base/common/severity.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';11import { ICommandService } from '../../../commands/common/commands.js';12import { ContextKeyExpr, ContextKeyExpression, IContext, IContextKeyService, IContextKeyServiceTarget } from '../../../contextkey/common/contextkey.js';13import { AbstractKeybindingService } from '../../common/abstractKeybindingService.js';14import { IKeyboardEvent } from '../../common/keybinding.js';15import { KeybindingResolver } from '../../common/keybindingResolver.js';16import { ResolvedKeybindingItem } from '../../common/resolvedKeybindingItem.js';17import { USLayoutResolvedKeybinding } from '../../common/usLayoutResolvedKeybinding.js';18import { createUSLayoutResolvedKeybinding } from './keybindingsTestUtils.js';19import { NullLogService } from '../../../log/common/log.js';20import { INotification, INotificationService, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification } from '../../../notification/common/notification.js';21import { NullTelemetryService } from '../../../telemetry/common/telemetryUtils.js';2223function createContext(ctx: any) {24return {25getValue: (key: string) => {26return ctx[key];27}28};29}3031suite('AbstractKeybindingService', () => {3233class TestKeybindingService extends AbstractKeybindingService {34private _resolver: KeybindingResolver;3536constructor(37resolver: KeybindingResolver,38contextKeyService: IContextKeyService,39commandService: ICommandService,40notificationService: INotificationService41) {42super(contextKeyService, commandService, NullTelemetryService, notificationService, new NullLogService());43this._resolver = resolver;44}4546protected _getResolver(): KeybindingResolver {47return this._resolver;48}4950protected _documentHasFocus(): boolean {51return true;52}5354public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] {55return USLayoutResolvedKeybinding.resolveKeybinding(kb, OS);56}5758public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {59const chord = new KeyCodeChord(60keyboardEvent.ctrlKey,61keyboardEvent.shiftKey,62keyboardEvent.altKey,63keyboardEvent.metaKey,64keyboardEvent.keyCode65).toKeybinding();66return this.resolveKeybinding(chord)[0];67}6869public resolveUserBinding(userBinding: string): ResolvedKeybinding[] {70return [];71}7273public testDispatch(kb: number): boolean {74const keybinding = createSimpleKeybinding(kb, OS);75return this._dispatch({76_standardKeyboardEventBrand: true,77ctrlKey: keybinding.ctrlKey,78shiftKey: keybinding.shiftKey,79altKey: keybinding.altKey,80metaKey: keybinding.metaKey,81altGraphKey: false,82keyCode: keybinding.keyCode,83code: null!84}, null!);85}8687public _dumpDebugInfo(): string {88return '';89}9091public _dumpDebugInfoJSON(): string {92return '';93}9495public registerSchemaContribution(): IDisposable {96return Disposable.None;97}9899public enableKeybindingHoldMode() {100return undefined;101}102}103104let createTestKeybindingService: (items: ResolvedKeybindingItem[], contextValue?: any) => TestKeybindingService = null!;105let currentContextValue: IContext | null = null;106let executeCommandCalls: { commandId: string; args: unknown[] }[] = null!;107let showMessageCalls: { sev: Severity; message: any }[] = null!;108let statusMessageCalls: string[] | null = null;109let statusMessageCallsDisposed: string[] | null = null;110111112teardown(() => {113currentContextValue = null;114executeCommandCalls = null!;115showMessageCalls = null!;116createTestKeybindingService = null!;117statusMessageCalls = null;118statusMessageCallsDisposed = null;119});120121ensureNoDisposablesAreLeakedInTestSuite();122123setup(() => {124executeCommandCalls = [];125showMessageCalls = [];126statusMessageCalls = [];127statusMessageCallsDisposed = [];128129createTestKeybindingService = (items: ResolvedKeybindingItem[]): TestKeybindingService => {130131const contextKeyService: IContextKeyService = {132_serviceBrand: undefined,133onDidChangeContext: undefined!,134bufferChangeEvents() { },135createKey: undefined!,136contextMatchesRules: (rules: ContextKeyExpression | null | undefined) => {137if (!rules) {138return true;139}140if (!currentContextValue) {141return false;142}143return rules.evaluate(currentContextValue);144},145getContextKeyValue: undefined!,146createScoped: undefined!,147createOverlay: undefined!,148getContext: (target: IContextKeyServiceTarget): any => {149return currentContextValue;150},151updateParent: () => { }152};153154const commandService: ICommandService = {155_serviceBrand: undefined,156onWillExecuteCommand: () => Disposable.None,157onDidExecuteCommand: () => Disposable.None,158executeCommand: (commandId: string, ...args: unknown[]): Promise<any> => {159executeCommandCalls.push({160commandId: commandId,161args: args162});163return Promise.resolve(undefined);164}165};166167const notificationService: INotificationService = {168_serviceBrand: undefined,169onDidChangeFilter: undefined!,170notify: (notification: INotification) => {171showMessageCalls.push({ sev: notification.severity, message: notification.message });172return new NoOpNotification();173},174info: (message: any) => {175showMessageCalls.push({ sev: Severity.Info, message });176return new NoOpNotification();177},178warn: (message: any) => {179showMessageCalls.push({ sev: Severity.Warning, message });180return new NoOpNotification();181},182error: (message: any) => {183showMessageCalls.push({ sev: Severity.Error, message });184return new NoOpNotification();185},186prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions) {187throw new Error('not implemented');188},189status(message: string, options?: IStatusMessageOptions) {190statusMessageCalls!.push(message);191return {192close: () => {193statusMessageCallsDisposed!.push(message);194}195};196},197setFilter() {198throw new Error('not implemented');199},200getFilter() {201throw new Error('not implemented');202},203getFilters() {204throw new Error('not implemented');205},206removeFilter() {207throw new Error('not implemented');208}209};210211const resolver = new KeybindingResolver(items, [], () => { });212213return new TestKeybindingService(resolver, contextKeyService, commandService, notificationService);214};215});216217function kbItem(keybinding: number | number[], command: string | null, when?: ContextKeyExpression): ResolvedKeybindingItem {218return new ResolvedKeybindingItem(219createUSLayoutResolvedKeybinding(keybinding, OS),220command,221null,222when,223true,224null,225false226);227}228229function toUsLabel(keybinding: number): string {230return createUSLayoutResolvedKeybinding(keybinding, OS)!.getLabel()!;231}232233suite('simple tests: single- and multi-chord keybindings are dispatched', () => {234235test('a single-chord keybinding is dispatched correctly; this test makes sure the dispatch in general works before we test empty-string/null command ID', () => {236237const key = KeyMod.CtrlCmd | KeyCode.KeyK;238const kbService = createTestKeybindingService([239kbItem(key, 'myCommand'),240]);241242currentContextValue = createContext({});243const shouldPreventDefault = kbService.testDispatch(key);244assert.deepStrictEqual(shouldPreventDefault, true);245assert.deepStrictEqual(executeCommandCalls, ([{ commandId: 'myCommand', args: [null] }]));246assert.deepStrictEqual(showMessageCalls, []);247assert.deepStrictEqual(statusMessageCalls, []);248assert.deepStrictEqual(statusMessageCallsDisposed, []);249250kbService.dispose();251});252253test('a multi-chord keybinding is dispatched correctly', () => {254255const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;256const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;257const key = [chord0, chord1];258const kbService = createTestKeybindingService([259kbItem(key, 'myCommand'),260]);261262currentContextValue = createContext({});263264let shouldPreventDefault = kbService.testDispatch(chord0);265assert.deepStrictEqual(shouldPreventDefault, true);266assert.deepStrictEqual(executeCommandCalls, []);267assert.deepStrictEqual(showMessageCalls, []);268assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));269assert.deepStrictEqual(statusMessageCallsDisposed, []);270271shouldPreventDefault = kbService.testDispatch(chord1);272assert.deepStrictEqual(shouldPreventDefault, true);273assert.deepStrictEqual(executeCommandCalls, ([{ commandId: 'myCommand', args: [null] }]));274assert.deepStrictEqual(showMessageCalls, []);275assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));276assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));277278kbService.dispose();279});280});281282suite('keybindings with empty-string/null command ID', () => {283284test('a single-chord keybinding with an empty string command ID unbinds the keybinding (shouldPreventDefault = false)', () => {285286const kbService = createTestKeybindingService([287kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),288kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, ''),289]);290291// send Ctrl/Cmd + K292currentContextValue = createContext({});293const shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);294assert.deepStrictEqual(shouldPreventDefault, false);295assert.deepStrictEqual(executeCommandCalls, []);296assert.deepStrictEqual(showMessageCalls, []);297assert.deepStrictEqual(statusMessageCalls, []);298assert.deepStrictEqual(statusMessageCallsDisposed, []);299300kbService.dispose();301});302303test('a single-chord keybinding with a null command ID unbinds the keybinding (shouldPreventDefault = false)', () => {304305const kbService = createTestKeybindingService([306kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),307kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, null),308]);309310// send Ctrl/Cmd + K311currentContextValue = createContext({});312const shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);313assert.deepStrictEqual(shouldPreventDefault, false);314assert.deepStrictEqual(executeCommandCalls, []);315assert.deepStrictEqual(showMessageCalls, []);316assert.deepStrictEqual(statusMessageCalls, []);317assert.deepStrictEqual(statusMessageCallsDisposed, []);318319kbService.dispose();320});321322test('a multi-chord keybinding with an empty-string command ID keeps the keybinding (shouldPreventDefault = true)', () => {323324const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;325const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;326const key = [chord0, chord1];327const kbService = createTestKeybindingService([328kbItem(key, 'myCommand'),329kbItem(key, ''),330]);331332currentContextValue = createContext({});333334let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);335assert.deepStrictEqual(shouldPreventDefault, true);336assert.deepStrictEqual(executeCommandCalls, []);337assert.deepStrictEqual(showMessageCalls, []);338assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));339assert.deepStrictEqual(statusMessageCallsDisposed, []);340341shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyI);342assert.deepStrictEqual(shouldPreventDefault, true);343assert.deepStrictEqual(executeCommandCalls, []);344assert.deepStrictEqual(showMessageCalls, []);345assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`, `The key combination (${toUsLabel(chord0)}, ${toUsLabel(chord1)}) is not a command.`]));346assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));347348kbService.dispose();349});350351test('a multi-chord keybinding with a null command ID keeps the keybinding (shouldPreventDefault = true)', () => {352353const chord0 = KeyMod.CtrlCmd | KeyCode.KeyK;354const chord1 = KeyMod.CtrlCmd | KeyCode.KeyI;355const key = [chord0, chord1];356const kbService = createTestKeybindingService([357kbItem(key, 'myCommand'),358kbItem(key, null),359]);360361currentContextValue = createContext({});362363let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);364assert.deepStrictEqual(shouldPreventDefault, true);365assert.deepStrictEqual(executeCommandCalls, []);366assert.deepStrictEqual(showMessageCalls, []);367assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));368assert.deepStrictEqual(statusMessageCallsDisposed, []);369370shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyI);371assert.deepStrictEqual(shouldPreventDefault, true);372assert.deepStrictEqual(executeCommandCalls, []);373assert.deepStrictEqual(showMessageCalls, []);374assert.deepStrictEqual(statusMessageCalls, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`, `The key combination (${toUsLabel(chord0)}, ${toUsLabel(chord1)}) is not a command.`]));375assert.deepStrictEqual(statusMessageCallsDisposed, ([`(${toUsLabel(chord0)}) was pressed. Waiting for second key of chord...`]));376377kbService.dispose();378});379380});381382test('issue #16498: chord mode is quit for invalid chords', () => {383384const kbService = createTestKeybindingService([385kbItem(KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyX), 'chordCommand'),386kbItem(KeyCode.Backspace, 'simpleCommand'),387]);388389// send Ctrl/Cmd + K390let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);391assert.strictEqual(shouldPreventDefault, true);392assert.deepStrictEqual(executeCommandCalls, []);393assert.deepStrictEqual(showMessageCalls, []);394assert.deepStrictEqual(statusMessageCalls, [395`(${toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK)}) was pressed. Waiting for second key of chord...`396]);397assert.deepStrictEqual(statusMessageCallsDisposed, []);398executeCommandCalls = [];399showMessageCalls = [];400statusMessageCalls = [];401statusMessageCallsDisposed = [];402403// send backspace404shouldPreventDefault = kbService.testDispatch(KeyCode.Backspace);405assert.strictEqual(shouldPreventDefault, true);406assert.deepStrictEqual(executeCommandCalls, []);407assert.deepStrictEqual(showMessageCalls, []);408assert.deepStrictEqual(statusMessageCalls, [409`The key combination (${toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK)}, ${toUsLabel(KeyCode.Backspace)}) is not a command.`410]);411assert.deepStrictEqual(statusMessageCallsDisposed, [412`(${toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK)}) was pressed. Waiting for second key of chord...`413]);414executeCommandCalls = [];415showMessageCalls = [];416statusMessageCalls = [];417statusMessageCallsDisposed = [];418419// send backspace420shouldPreventDefault = kbService.testDispatch(KeyCode.Backspace);421assert.strictEqual(shouldPreventDefault, true);422assert.deepStrictEqual(executeCommandCalls, [{423commandId: 'simpleCommand',424args: [null]425}]);426assert.deepStrictEqual(showMessageCalls, []);427assert.deepStrictEqual(statusMessageCalls, []);428assert.deepStrictEqual(statusMessageCallsDisposed, []);429executeCommandCalls = [];430showMessageCalls = [];431statusMessageCalls = [];432statusMessageCallsDisposed = [];433434kbService.dispose();435});436437test('issue #16833: Keybinding service should not testDispatch on modifier keys', () => {438439const kbService = createTestKeybindingService([440kbItem(KeyCode.Ctrl, 'nope'),441kbItem(KeyCode.Meta, 'nope'),442kbItem(KeyCode.Alt, 'nope'),443kbItem(KeyCode.Shift, 'nope'),444445kbItem(KeyMod.CtrlCmd, 'nope'),446kbItem(KeyMod.WinCtrl, 'nope'),447kbItem(KeyMod.Alt, 'nope'),448kbItem(KeyMod.Shift, 'nope'),449]);450451function assertIsIgnored(keybinding: number): void {452const shouldPreventDefault = kbService.testDispatch(keybinding);453assert.strictEqual(shouldPreventDefault, false);454assert.deepStrictEqual(executeCommandCalls, []);455assert.deepStrictEqual(showMessageCalls, []);456assert.deepStrictEqual(statusMessageCalls, []);457assert.deepStrictEqual(statusMessageCallsDisposed, []);458executeCommandCalls = [];459showMessageCalls = [];460statusMessageCalls = [];461statusMessageCallsDisposed = [];462}463464assertIsIgnored(KeyCode.Ctrl);465assertIsIgnored(KeyCode.Meta);466assertIsIgnored(KeyCode.Alt);467assertIsIgnored(KeyCode.Shift);468469assertIsIgnored(KeyMod.CtrlCmd);470assertIsIgnored(KeyMod.WinCtrl);471assertIsIgnored(KeyMod.Alt);472assertIsIgnored(KeyMod.Shift);473474kbService.dispose();475});476477test('can trigger command that is sharing keybinding with chord', () => {478479const kbService = createTestKeybindingService([480kbItem(KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyX), 'chordCommand'),481kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'simpleCommand', ContextKeyExpr.has('key1')),482]);483484485// send Ctrl/Cmd + K486currentContextValue = createContext({487key1: true488});489let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);490assert.strictEqual(shouldPreventDefault, true);491assert.deepStrictEqual(executeCommandCalls, [{492commandId: 'simpleCommand',493args: [null]494}]);495assert.deepStrictEqual(showMessageCalls, []);496assert.deepStrictEqual(statusMessageCalls, []);497assert.deepStrictEqual(statusMessageCallsDisposed, []);498executeCommandCalls = [];499showMessageCalls = [];500statusMessageCalls = [];501statusMessageCallsDisposed = [];502503// send Ctrl/Cmd + K504currentContextValue = createContext({});505shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);506assert.strictEqual(shouldPreventDefault, true);507assert.deepStrictEqual(executeCommandCalls, []);508assert.deepStrictEqual(showMessageCalls, []);509assert.deepStrictEqual(statusMessageCalls, [510`(${toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK)}) was pressed. Waiting for second key of chord...`511]);512assert.deepStrictEqual(statusMessageCallsDisposed, []);513executeCommandCalls = [];514showMessageCalls = [];515statusMessageCalls = [];516statusMessageCallsDisposed = [];517518// send Ctrl/Cmd + X519currentContextValue = createContext({});520shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyX);521assert.strictEqual(shouldPreventDefault, true);522assert.deepStrictEqual(executeCommandCalls, [{523commandId: 'chordCommand',524args: [null]525}]);526assert.deepStrictEqual(showMessageCalls, []);527assert.deepStrictEqual(statusMessageCalls, []);528assert.deepStrictEqual(statusMessageCallsDisposed, [529`(${toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK)}) was pressed. Waiting for second key of chord...`530]);531executeCommandCalls = [];532showMessageCalls = [];533statusMessageCalls = [];534statusMessageCallsDisposed = [];535536kbService.dispose();537});538539test('cannot trigger chord if command is overwriting', () => {540541const kbService = createTestKeybindingService([542kbItem(KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyX), 'chordCommand', ContextKeyExpr.has('key1')),543kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'simpleCommand'),544]);545546547// send Ctrl/Cmd + K548currentContextValue = createContext({});549let shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);550assert.strictEqual(shouldPreventDefault, true);551assert.deepStrictEqual(executeCommandCalls, [{552commandId: 'simpleCommand',553args: [null]554}]);555assert.deepStrictEqual(showMessageCalls, []);556assert.deepStrictEqual(statusMessageCalls, []);557assert.deepStrictEqual(statusMessageCallsDisposed, []);558executeCommandCalls = [];559showMessageCalls = [];560statusMessageCalls = [];561statusMessageCallsDisposed = [];562563// send Ctrl/Cmd + K564currentContextValue = createContext({565key1: true566});567shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);568assert.strictEqual(shouldPreventDefault, true);569assert.deepStrictEqual(executeCommandCalls, [{570commandId: 'simpleCommand',571args: [null]572}]);573assert.deepStrictEqual(showMessageCalls, []);574assert.deepStrictEqual(statusMessageCalls, []);575assert.deepStrictEqual(statusMessageCallsDisposed, []);576executeCommandCalls = [];577showMessageCalls = [];578statusMessageCalls = [];579statusMessageCallsDisposed = [];580581// send Ctrl/Cmd + X582currentContextValue = createContext({583key1: true584});585shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyX);586assert.strictEqual(shouldPreventDefault, false);587assert.deepStrictEqual(executeCommandCalls, []);588assert.deepStrictEqual(showMessageCalls, []);589assert.deepStrictEqual(statusMessageCalls, []);590assert.deepStrictEqual(statusMessageCallsDisposed, []);591executeCommandCalls = [];592showMessageCalls = [];593statusMessageCalls = [];594statusMessageCallsDisposed = [];595596kbService.dispose();597});598599test('can have spying command', () => {600601const kbService = createTestKeybindingService([602kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, '^simpleCommand'),603]);604605// send Ctrl/Cmd + K606currentContextValue = createContext({});607const shouldPreventDefault = kbService.testDispatch(KeyMod.CtrlCmd | KeyCode.KeyK);608assert.strictEqual(shouldPreventDefault, false);609assert.deepStrictEqual(executeCommandCalls, [{610commandId: 'simpleCommand',611args: [null]612}]);613assert.deepStrictEqual(showMessageCalls, []);614assert.deepStrictEqual(statusMessageCalls, []);615assert.deepStrictEqual(statusMessageCallsDisposed, []);616executeCommandCalls = [];617showMessageCalls = [];618statusMessageCalls = [];619statusMessageCallsDisposed = [];620621kbService.dispose();622});623624suite('appendKeybinding', () => {625test('appends keybinding label when command has a keybinding', () => {626const kbService = createTestKeybindingService([627kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),628]);629630const result = kbService.appendKeybinding('My Label', 'myCommand');631const expectedLabel = toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK);632assert.strictEqual(result, `My Label (${expectedLabel})`);633634kbService.dispose();635});636637test('returns only label when command has no keybinding', () => {638const kbService = createTestKeybindingService([]);639640const result = kbService.appendKeybinding('My Label', 'myCommand');641assert.strictEqual(result, 'My Label');642643kbService.dispose();644});645646test('returns only label when commandId is null', () => {647const kbService = createTestKeybindingService([648kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),649]);650651const result = kbService.appendKeybinding('My Label', null);652assert.strictEqual(result, 'My Label');653654kbService.dispose();655});656657test('returns only label when commandId is undefined', () => {658const kbService = createTestKeybindingService([659kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),660]);661662const result = kbService.appendKeybinding('My Label', undefined);663assert.strictEqual(result, 'My Label');664665kbService.dispose();666});667668test('returns only label when commandId is empty string', () => {669const kbService = createTestKeybindingService([670kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),671]);672673const result = kbService.appendKeybinding('My Label', '');674assert.strictEqual(result, 'My Label');675676kbService.dispose();677});678679test('appends keybinding for command with context when context matches', () => {680const kbService = createTestKeybindingService([681kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand', ContextKeyExpr.has('key1')),682]);683684currentContextValue = createContext({ key1: true });685const result = kbService.appendKeybinding('My Label', 'myCommand');686const expectedLabel = toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK);687assert.strictEqual(result, `My Label (${expectedLabel})`);688689kbService.dispose();690});691692test('returns only label when context does not match and enforceContextCheck is true', () => {693const kbService = createTestKeybindingService([694kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand', ContextKeyExpr.has('key1')),695]);696697currentContextValue = createContext({});698const result = kbService.appendKeybinding('My Label', 'myCommand', undefined, true);699assert.strictEqual(result, 'My Label');700701kbService.dispose();702});703704test('appends keybinding when context does not match but enforceContextCheck is false', () => {705const kbService = createTestKeybindingService([706kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand', ContextKeyExpr.has('key1')),707]);708709currentContextValue = createContext({});710const result = kbService.appendKeybinding('My Label', 'myCommand', undefined, false);711const expectedLabel = toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK);712assert.strictEqual(result, `My Label (${expectedLabel})`);713714kbService.dispose();715});716717test('appends keybinding even when label is empty string', () => {718const kbService = createTestKeybindingService([719kbItem(KeyMod.CtrlCmd | KeyCode.KeyK, 'myCommand'),720]);721722const result = kbService.appendKeybinding('', 'myCommand');723const expectedLabel = toUsLabel(KeyMod.CtrlCmd | KeyCode.KeyK);724assert.strictEqual(result, ` (${expectedLabel})`);725726kbService.dispose();727});728});729});730731732