Path: blob/main/src/vs/editor/contrib/codeAction/test/browser/codeActionModel.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 { promiseWithResolvers } from '../../../../../base/common/async.js';7import { assertType } from '../../../../../base/common/types.js';8import { URI } from '../../../../../base/common/uri.js';9import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';11import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js';12import { MarkerService } from '../../../../../platform/markers/common/markerService.js';13import { ICodeEditor } from '../../../../browser/editorBrowser.js';14import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js';15import * as languages from '../../../../common/languages.js';16import { TextModel } from '../../../../common/model/textModel.js';17import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js';18import { createTextModel } from '../../../../test/common/testTextModel.js';19import { CodeActionModel, CodeActionsState } from '../../browser/codeActionModel.js';2021const testProvider = {22provideCodeActions(): languages.CodeActionList {23return {24actions: [25{ title: 'test', command: { id: 'test-command', title: 'test', arguments: [] } }26],27dispose() { /* noop*/ }28};29}30};3132suite('CodeActionModel', () => {3334const languageId = 'foo-lang';35const uri = URI.parse('untitled:path');36let model: TextModel;37let markerService: MarkerService;38let editor: ICodeEditor;39let registry: LanguageFeatureRegistry<languages.CodeActionProvider>;4041setup(() => {42markerService = new MarkerService();43model = createTextModel('foobar foo bar\nfarboo far boo', languageId, undefined, uri);44editor = createTestCodeEditor(model);45editor.setPosition({ lineNumber: 1, column: 1 });46registry = new LanguageFeatureRegistry();47});4849const store = ensureNoDisposablesAreLeakedInTestSuite();5051teardown(() => {52editor.dispose();53model.dispose();54markerService.dispose();55});5657test('Oracle -> marker added', async () => {58const { promise: donePromise, resolve: done } = promiseWithResolvers<void>();5960await runWithFakedTimers({ useFakeTimers: true }, () => {61const reg = registry.register(languageId, testProvider);62store.add(reg);6364const contextKeys = new MockContextKeyService();65const model = store.add(new CodeActionModel(editor, registry, markerService, contextKeys, undefined));66store.add(model.onDidChangeState((e: CodeActionsState.State) => {67assertType(e.type === CodeActionsState.Type.Triggered);6869assert.strictEqual(e.trigger.type, languages.CodeActionTriggerType.Auto);70assert.ok(e.actions);7172e.actions.then(fixes => {73model.dispose();74assert.strictEqual(fixes.validActions.length, 1);75done();76}, done);77}));7879// start here80markerService.changeOne('fake', uri, [{81startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,82message: 'error',83severity: 1,84code: '',85source: ''86}]);87return donePromise;88});89});9091test('Oracle -> position changed', async () => {92await runWithFakedTimers({ useFakeTimers: true }, () => {93const reg = registry.register(languageId, testProvider);94store.add(reg);9596markerService.changeOne('fake', uri, [{97startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,98message: 'error',99severity: 1,100code: '',101source: ''102}]);103104editor.setPosition({ lineNumber: 2, column: 1 });105106return new Promise((resolve, reject) => {107const contextKeys = new MockContextKeyService();108const model = store.add(new CodeActionModel(editor, registry, markerService, contextKeys, undefined));109store.add(model.onDidChangeState((e: CodeActionsState.State) => {110assertType(e.type === CodeActionsState.Type.Triggered);111112assert.strictEqual(e.trigger.type, languages.CodeActionTriggerType.Auto);113assert.ok(e.actions);114e.actions.then(fixes => {115model.dispose();116assert.strictEqual(fixes.validActions.length, 1);117resolve(undefined);118}, reject);119}));120// start here121editor.setPosition({ lineNumber: 1, column: 1 });122});123});124});125126test('Oracle -> should only auto trigger once for cursor and marker update right after each other', async () => {127const { promise: donePromise, resolve: done } = promiseWithResolvers<void>();128await runWithFakedTimers({ useFakeTimers: true }, () => {129const reg = registry.register(languageId, testProvider);130store.add(reg);131132let triggerCount = 0;133const contextKeys = new MockContextKeyService();134const model = store.add(new CodeActionModel(editor, registry, markerService, contextKeys, undefined));135store.add(model.onDidChangeState((e: CodeActionsState.State) => {136assertType(e.type === CodeActionsState.Type.Triggered);137138assert.strictEqual(e.trigger.type, languages.CodeActionTriggerType.Auto);139++triggerCount;140141// give time for second trigger before completing test142setTimeout(() => {143model.dispose();144assert.strictEqual(triggerCount, 1);145done();146}, 0);147}, 5 /*delay*/));148149markerService.changeOne('fake', uri, [{150startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,151message: 'error',152severity: 1,153code: '',154source: ''155}]);156157editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 4, endColumn: 1 });158159return donePromise;160});161});162});163164165