diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 4f7ba0d46c81a..6b3d35df9ffd6 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -12,6 +12,7 @@ import { IAsyncDataSource, ITreeNode } from '../../../../browser/ui/tree/tree.js import { timeout } from '../../../../common/async.js'; import { Iterable } from '../../../../common/iterator.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js'; +import { runWithFakedTimers } from '../../../common/timeTravelScheduler.js'; interface Element { id: string; @@ -465,45 +466,47 @@ suite('AsyncDataTree', function () { }); test('issue #80098 - first expand should call getChildren', async () => { - const container = document.createElement('div'); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + const container = document.createElement('div'); - const calls: Function[] = []; - const dataSource = new class implements IAsyncDataSource { - hasChildren(element: Element): boolean { - return !!element.children && element.children.length > 0; - } - getChildren(element: Element): Promise { - return new Promise(c => calls.push(() => c(element.children || []))); - } - }; + const calls: Function[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + return new Promise(c => calls.push(() => c(element.children || []))); + } + }; - const model = new Model({ - id: 'root', - children: [{ - id: 'a', children: [{ - id: 'aa' + const model = new Model({ + id: 'root', + children: [{ + id: 'a', children: [{ + id: 'aa' + }] }] - }] - }); + }); - const tree = store.add(new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() })); - tree.layout(200); + const tree = store.add(new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() })); + tree.layout(200); - const pSetInput = tree.setInput(model.root); - calls.pop()!(); // resolve getChildren(root) - await pSetInput; + const pSetInput = tree.setInput(model.root); + calls.pop()!(); // resolve getChildren(root) + await pSetInput; - const pExpandA = tree.expand(model.get('a')); - assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)'); + const pExpandA = tree.expand(model.get('a')); + assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)'); - let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); - assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done'); + let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); + assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done'); - calls.pop()!(); - assert.strictEqual(calls.length, 0, 'no pending getChildren calls'); + calls.pop()!(); + assert.strictEqual(calls.length, 0, 'no pending getChildren calls'); - race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); - assert.strictEqual(race, 'expand', 'expand(a) should now be done'); + race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); + assert.strictEqual(race, 'expand', 'expand(a) should now be done'); + }); }); test('issue #78388 - tree should react to hasChildren toggles', async () => { diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 7aa83e5df457f..294a8286dbbde 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -56,6 +56,7 @@ import { CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/c import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IExtHostTelemetry } from '../../common/extHostTelemetry.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { runWithFakedTimers } from '../../../../base/test/common/timeTravelScheduler.js'; suite('ExtHostLanguageFeatures', function () { @@ -233,62 +234,65 @@ suite('ExtHostLanguageFeatures', function () { // --- code lens test('CodeLens, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses(): any { + throw new Error('evil'); + } + })); + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses(): any { - throw new Error('evil'); - } - })); - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses() { - return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + value.dispose(); + }); }); test('CodeLens, do not resolve a resolved lens', async () => { - - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses(): any { - return [new types.CodeLens( - new types.Range(0, 0, 0, 0), - { command: 'id', title: 'Title' })]; - } - resolveCodeLens(): any { - assert.ok(false, 'do not resolve'); - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - const [data] = value.lenses; - const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); - assert.strictEqual(symbol!.command!.id, 'id'); - assert.strictEqual(symbol!.command!.title, 'Title'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses(): any { + return [new types.CodeLens( + new types.Range(0, 0, 0, 0), + { command: 'id', title: 'Title' })]; + } + resolveCodeLens(): any { + assert.ok(false, 'do not resolve'); + } + })); + + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + const [data] = value.lenses; + const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); + assert.strictEqual(symbol!.command!.id, 'id'); + assert.strictEqual(symbol!.command!.title, 'Title'); + value.dispose(); + }); }); test('CodeLens, missing command', async () => { - - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses() { - return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - const [data] = value.lenses; - const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); - assert.strictEqual(symbol, undefined); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + const [data] = value.lenses; + const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); + assert.strictEqual(symbol, undefined); + value.dispose(); + }); }); // --- definition @@ -636,92 +640,96 @@ suite('ExtHostLanguageFeatures', function () { // --- quick fix test('Quick Fix, command data conversion', async () => { - - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { - provideCodeActions(): vscode.Command[] { - return [ - { command: 'test1', title: 'Testing1' }, - { command: 'test2', title: 'Testing2' } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 2); - const [first, second] = actions; - assert.strictEqual(first.action.title, 'Testing1'); - assert.strictEqual(first.action.command!.id, 'test1'); - assert.strictEqual(second.action.title, 'Testing2'); - assert.strictEqual(second.action.command!.id, 'test2'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { + provideCodeActions(): vscode.Command[] { + return [ + { command: 'test1', title: 'Testing1' }, + { command: 'test2', title: 'Testing2' } + ]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 2); + const [first, second] = actions; + assert.strictEqual(first.action.title, 'Testing1'); + assert.strictEqual(first.action.command!.id, 'test1'); + assert.strictEqual(second.action.title, 'Testing2'); + assert.strictEqual(second.action.command!.id, 'test2'); + value.dispose(); + }); }); test('Quick Fix, code action data conversion', async () => { - - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { - provideCodeActions(): vscode.CodeAction[] { - return [ - { - title: 'Testing1', - command: { title: 'Testing1Command', command: 'test1' }, - kind: types.CodeActionKind.Empty.append('test.scope') - } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - const [first] = actions; - assert.strictEqual(first.action.title, 'Testing1'); - assert.strictEqual(first.action.command!.title, 'Testing1Command'); - assert.strictEqual(first.action.command!.id, 'test1'); - assert.strictEqual(first.action.kind, 'test.scope'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { + provideCodeActions(): vscode.CodeAction[] { + return [ + { + title: 'Testing1', + command: { title: 'Testing1Command', command: 'test1' }, + kind: types.CodeActionKind.Empty.append('test.scope') + } + ]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + const [first] = actions; + assert.strictEqual(first.action.title, 'Testing1'); + assert.strictEqual(first.action.command!.title, 'Testing1Command'); + assert.strictEqual(first.action.command!.id, 'test1'); + assert.strictEqual(first.action.kind, 'test.scope'); + value.dispose(); + }); }); test('Cannot read property \'id\' of undefined, #29469', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + return [ + undefined, + null, + { command: 'test', title: 'Testing' } + ]; + } + })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - return [ - undefined, - null, - { command: 'test', title: 'Testing' } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + value.dispose(); + }); }); test('Quick Fix, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + throw new Error('evil'); + } + })); + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + return [{ command: 'test', title: 'Testing' }]; + } + })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - throw new Error('evil'); - } - })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - return [{ command: 'test', title: 'Testing' }]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + value.dispose(); + }); }); // --- navigate types @@ -965,103 +973,107 @@ suite('ExtHostLanguageFeatures', function () { // --- suggestions test('Suggest, order 1/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing1')]; + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing1')]; - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing2')]; - } - }, [])); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing2')]; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 1); - assert.strictEqual(value.items[0].completion.insertText, 'testing2'); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 1); + assert.strictEqual(value.items[0].completion.insertText, 'testing2'); + value.disposable.dispose(); + }); }); test('Suggest, order 2/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('weak-selector')]; // weaker selector but result + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('weak-selector')]; // weaker selector but result - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return []; // stronger selector but not a good result; - } - }, [])); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return []; // stronger selector but not a good result; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 1); - assert.strictEqual(value.items[0].completion.insertText, 'weak-selector'); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 1); + assert.strictEqual(value.items[0].completion.insertText, 'weak-selector'); + value.disposable.dispose(); + }); }); test('Suggest, order 3/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-1')]; + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('strong-1')]; - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('strong-2')]; - } - }, [])); - - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 2); - assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label - assert.strictEqual(value.items[1].completion.insertText, 'strong-2'); - value.disposable.dispose(); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-2')]; + } + }, [])); + + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 2); + assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label + assert.strictEqual(value.items[1].completion.insertText, 'strong-2'); + value.disposable.dispose(); + }); }); test('Suggest, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + throw new Error('evil'); + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - throw new Error('evil'); - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing')]; - } - }, [])); - + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing')]; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items[0].container.incomplete, false); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items[0].container.incomplete, false); + value.disposable.dispose(); + }); }); test('Suggest, CompletionList', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return new types.CompletionList([new types.CompletionItem('hello')], true); + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return new types.CompletionList([new types.CompletionItem('hello')], true); - } - }, [])); - - await rpcProtocol.sync(); - await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))).then(model => { - assert.strictEqual(model.items[0].container.incomplete, true); - model.disposable.dispose(); + await rpcProtocol.sync(); + await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))).then(model => { + assert.strictEqual(model.items[0].container.incomplete, true); + model.disposable.dispose(); + }); }); }); diff --git a/test/unit/browser/renderer.html b/test/unit/browser/renderer.html index 45c315c9da02c..e94cf77a6f8f1 100644 --- a/test/unit/browser/renderer.html +++ b/test/unit/browser/renderer.html @@ -34,6 +34,46 @@ const urlParams = new URLSearchParams(window.location.search); const isCI = urlParams.get('ci'); + const $globalThis = globalThis; + const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts); + + /** + * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-. + * + * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay + * that browsers set when the nesting level is > 5. + */ + const setTimeout0 = (() => { + if (setTimeout0IsFaster) { + const pending = []; + + $globalThis.addEventListener('message', (e) => { + if (e.data && e.data.vscodeScheduleAsyncWork) { + for (let i = 0, len = pending.length; i < len; i++) { + const candidate = pending[i]; + if (candidate.id === e.data.vscodeScheduleAsyncWork) { + pending.splice(i, 1); + candidate.callback(); + return; + } + } + } + }); + let lastId = 0; + return (callback) => { + const myId = ++lastId; + pending.push({ + id: myId, + callback: callback + }); + $globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*'); + }; + } + return (callback) => setTimeout(callback); + })(); + + Mocha.Runner.immediately = setTimeout0; + mocha.setup({ ui: 'tdd', timeout: isCI ? 30000 : 5000 diff --git a/test/unit/electron/renderer.html b/test/unit/electron/renderer.html index a8d0dd98abeea..4ae458986e6ce 100644 --- a/test/unit/electron/renderer.html +++ b/test/unit/electron/renderer.html @@ -32,12 +32,6 @@ e.stopPropagation(); } }); - - mocha.setup({ - ui: 'tdd', - timeout: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' ? 30000 : 5000, - forbidOnly: typeof process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] === 'string' // disallow .only() when running on build machine - });