completions.test.ts 16.9 KB
Newer Older
M
Matt Bierner 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'mocha';
import * as vscode from 'vscode';
8 9 10
import { disposeAll } from '../../utils/dispose';
import { acceptFirstSuggestion, typeCommitCharacter } from '../../test/suggestTestHelpers';
import { assertEditorContents, Config, createTestEditor, enumerateConfig, joinLines, updateConfig, VsCodeConfiguration } from '../../test/testUtils';
M
Matt Bierner 已提交
11 12 13

const testDocumentUri = vscode.Uri.parse('untitled:test.ts');

14 15
const insertModes = Object.freeze(['insert', 'replace']);

M
Matt Bierner 已提交
16
suite.skip('TypeScript Completions', () => {
M
Matt Bierner 已提交
17
	const configDefaults: VsCodeConfiguration = Object.freeze({
M
Matt Bierner 已提交
18
		[Config.autoClosingBrackets]: 'always',
19
		[Config.typescriptCompleteFunctionCalls]: false,
M
Matt Bierner 已提交
20
		[Config.insertMode]: 'insert',
21 22
		[Config.snippetSuggestions]: 'none',
		[Config.suggestSelection]: 'first',
23 24
		[Config.javascriptQuoteStyle]: 'double',
		[Config.typescriptQuoteStyle]: 'double',
M
Matt Bierner 已提交
25 26
	});

27
	const _disposables: vscode.Disposable[] = [];
28 29 30
	let oldConfig: { [key: string]: any } = {};

	setup(async () => {
M
Matt Bierner 已提交
31 32
		// the tests assume that typescript features are registered
		await vscode.extensions.getExtension('vscode.typescript-language-features')!.activate();
M
Matt Bierner 已提交
33

M
Matt Bierner 已提交
34
		// Save off config and apply defaults
35
		oldConfig = await updateConfig(testDocumentUri, configDefaults);
M
Matt Bierner 已提交
36 37
	});

38
	teardown(async () => {
M
Matt Bierner 已提交
39
		disposeAll(_disposables);
40 41

		// Restore config
42
		await updateConfig(testDocumentUri, oldConfig);
43

M
Matt Bierner 已提交
44 45 46
		return vscode.commands.executeCommand('workbench.action.closeAllEditors');
	});

47
	test('Basic var completion', async () => {
M
Matt Bierner 已提交
48
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
49
			const editor = await createTestEditor(testDocumentUri,
50
				`const abcdef = 123;`,
51 52 53
				`ab$0;`
			);

54
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
55

56
			assertEditorContents(editor,
57 58 59 60 61 62 63
				joinLines(
					`const abcdef = 123;`,
					`abcdef;`
				),
				`config: ${config}`
			);
		});
64 65
	});

66
	test('Should treat period as commit character for var completions', async () => {
M
Matt Bierner 已提交
67
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
68
			const editor = await createTestEditor(testDocumentUri,
69
				`const abcdef = 123;`,
70 71 72
				`ab$0;`
			);

73 74 75
			await typeCommitCharacter(testDocumentUri, '.', _disposables);

			assertEditorContents(editor,
76 77 78 79 80 81
				joinLines(
					`const abcdef = 123;`,
					`abcdef.;`
				),
				`config: ${config}`);
		});
82 83
	});

84
	test('Should treat paren as commit character for function completions', async () => {
M
Matt Bierner 已提交
85
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
86
			const editor = await createTestEditor(testDocumentUri,
87
				`function abcdef() {};`,
88 89 90
				`ab$0;`
			);

91 92 93
			await typeCommitCharacter(testDocumentUri, '(', _disposables);

			assertEditorContents(editor,
94 95 96 97 98
				joinLines(
					`function abcdef() {};`,
					`abcdef();`
				), `config: ${config}`);
		});
99 100
	});

101
	test('Should insert backets when completing dot properties with spaces in name', async () => {
M
Matt Bierner 已提交
102
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
103
			const editor = await createTestEditor(testDocumentUri,
104
				'const x = { "hello world": 1 };',
105 106 107
				'x.$0'
			);

108
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
109

110
			assertEditorContents(editor,
111 112 113 114 115
				joinLines(
					'const x = { "hello world": 1 };',
					'x["hello world"]'
				), `config: ${config}`);
		});
116 117
	});

118 119 120 121 122
	test('Should allow commit characters for backet completions', async () => {
		for (const { char, insert } of [
			{ char: '.', insert: '.' },
			{ char: '(', insert: '()' },
		]) {
123
			const editor = await createTestEditor(testDocumentUri,
124
				'const x = { "hello world2": 1 };',
125 126 127
				'x.$0'
			);

128 129 130
			await typeCommitCharacter(testDocumentUri, char, _disposables);

			assertEditorContents(editor,
131 132 133 134
				joinLines(
					'const x = { "hello world2": 1 };',
					`x["hello world2"]${insert}`
				));
135 136 137

			disposeAll(_disposables);
			await vscode.commands.executeCommand('workbench.action.closeAllEditors');
138
		}
139 140
	});

M
Matt Bierner 已提交
141
	test('Should not prioritize bracket accessor completions. #63100', async () => {
M
Matt Bierner 已提交
142
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
143
			// 'a' should be first entry in completion list
144
			const editor = await createTestEditor(testDocumentUri,
145
				'const x = { "z-z": 1, a: 1 };',
146 147 148
				'x.$0'
			);

149
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
150

151
			assertEditorContents(editor,
152 153 154 155 156 157
				joinLines(
					'const x = { "z-z": 1, a: 1 };',
					'x.a'
				),
				`config: ${config}`);
		});
M
Matt Bierner 已提交
158
	});
M
Matt Bierner 已提交
159 160

	test('Accepting a string completion should replace the entire string. #53962', async () => {
161
		const editor = await createTestEditor(testDocumentUri,
M
Matt Bierner 已提交
162 163 164 165 166 167 168 169
			'interface TFunction {',
			`  (_: 'abc.abc2', __ ?: {}): string;`,
			`  (_: 'abc.abc', __?: {}): string;`,
			`}`,
			'const f: TFunction = (() => { }) as any;',
			`f('abc.abc$0')`
		);

170
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
171

172
		assertEditorContents(editor,
M
Matt Bierner 已提交
173 174 175 176 177 178 179 180 181 182
			joinLines(
				'interface TFunction {',
				`  (_: 'abc.abc2', __ ?: {}): string;`,
				`  (_: 'abc.abc', __?: {}): string;`,
				`}`,
				'const f: TFunction = (() => { }) as any;',
				`f('abc.abc')`
			));
	});

183
	test('completeFunctionCalls should complete function parameters when at end of word', async () => {
184
		await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true });
185

186
		// Complete with-in word
187
		const editor = await createTestEditor(testDocumentUri,
188 189
			`function abcdef(x, y, z) { }`,
			`abcdef$0`
190 191
		);

192
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
193

194
		assertEditorContents(editor,
195
			joinLines(
196 197 198 199 200 201
				`function abcdef(x, y, z) { }`,
				`abcdef(x, y, z)`
			));
	});

	test.skip('completeFunctionCalls should complete function parameters when within word', async () => {
202
		await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true });
203

204
		const editor = await createTestEditor(testDocumentUri,
205 206 207 208
			`function abcdef(x, y, z) { }`,
			`abcd$0ef`
		);

209
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
210

211
		assertEditorContents(editor,
212 213 214 215 216 217 218
			joinLines(
				`function abcdef(x, y, z) { }`,
				`abcdef(x, y, z)`
			));
	});

	test('completeFunctionCalls should not complete function parameters at end of word if we are already in something that looks like a function call, #18131', async () => {
219
		await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true });
220

221
		const editor = await createTestEditor(testDocumentUri,
222 223 224 225
			`function abcdef(x, y, z) { }`,
			`abcdef$0(1, 2, 3)`
		);

226
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
227

228
		assertEditorContents(editor,
229 230 231 232 233 234 235
			joinLines(
				`function abcdef(x, y, z) { }`,
				`abcdef(1, 2, 3)`
			));
	});

	test.skip('completeFunctionCalls should not complete function parameters within word if we are already in something that looks like a function call, #18131', async () => {
236
		await updateConfig(testDocumentUri, { [Config.typescriptCompleteFunctionCalls]: true });
237

238
		const editor = await createTestEditor(testDocumentUri,
239 240 241 242
			`function abcdef(x, y, z) { }`,
			`abcd$0ef(1, 2, 3)`
		);

243
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
244

245
		assertEditorContents(editor,
246 247 248
			joinLines(
				`function abcdef(x, y, z) { }`,
				`abcdef(1, 2, 3)`
249 250
			));
	});
251

252
	test('should not de-prioritize `this.member` suggestion, #74164', async () => {
M
Matt Bierner 已提交
253
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
254
			const editor = await createTestEditor(testDocumentUri,
255 256 257
				`class A {`,
				`  private detail = '';`,
				`  foo() {`,
258
				`    det$0`,
259 260
				`  }`,
				`}`,
261 262
			);

263
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
264

265
			assertEditorContents(editor,
266 267 268 269 270 271 272 273 274 275
				joinLines(
					`class A {`,
					`  private detail = '';`,
					`  foo() {`,
					`    this.detail`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
276
	});
M
Matt Bierner 已提交
277

278
	test('Member completions for string property name should insert `this.` and use brackets', async () => {
M
Matt Bierner 已提交
279
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
280
			const editor = await createTestEditor(testDocumentUri,
281 282 283 284 285 286 287 288
				`class A {`,
				`  ['xyz 123'] = 1`,
				`  foo() {`,
				`    xyz$0`,
				`  }`,
				`}`,
			);

289
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
290

291
			assertEditorContents(editor,
292 293 294 295 296 297 298 299 300 301 302 303
				joinLines(
					`class A {`,
					`  ['xyz 123'] = 1`,
					`  foo() {`,
					`    this["xyz 123"]`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});

304
	test('Member completions for string property name already using `this.` should add brackets', async () => {
M
Matt Bierner 已提交
305
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
306
			const editor = await createTestEditor(testDocumentUri,
307 308 309 310 311 312 313 314
				`class A {`,
				`  ['xyz 123'] = 1`,
				`  foo() {`,
				`    this.xyz$0`,
				`  }`,
				`}`,
			);

315
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
316

317
			assertEditorContents(editor,
318 319 320 321 322 323 324 325 326 327 328 329 330
				joinLines(
					`class A {`,
					`  ['xyz 123'] = 1`,
					`  foo() {`,
					`    this["xyz 123"]`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});

	test('Accepting a completion in word using `insert` mode should insert', async () => {
331
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
M
Matt Bierner 已提交
332

333
		const editor = await createTestEditor(testDocumentUri,
M
Matt Bierner 已提交
334 335 336 337
			`const abc = 123;`,
			`ab$0c`
		);

338
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
339

340
		assertEditorContents(editor,
M
Matt Bierner 已提交
341 342 343 344 345 346
			joinLines(
				`const abc = 123;`,
				`abcc`
			));
	});

347
	test('Accepting a completion in word using `replace` mode should replace', async () => {
348
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
M
Matt Bierner 已提交
349

350
		const editor = await createTestEditor(testDocumentUri,
M
Matt Bierner 已提交
351 352 353 354
			`const abc = 123;`,
			`ab$0c`
		);

355
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
356

357
		assertEditorContents(editor,
M
Matt Bierner 已提交
358 359 360 361 362
			joinLines(
				`const abc = 123;`,
				`abc`
			));
	});
363

364
	test('Accepting a member completion in word using `insert` mode add `this.` and insert', async () => {
365
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
366

367
		const editor = await createTestEditor(testDocumentUri,
368 369 370 371 372 373 374 375
			`class Foo {`,
			`  abc = 1;`,
			`  foo() {`,
			`    ab$0c`,
			`  }`,
			`}`,
		);

376
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
377

378
		assertEditorContents(editor,
379 380 381 382 383 384 385 386 387 388
			joinLines(
				`class Foo {`,
				`  abc = 1;`,
				`  foo() {`,
				`    this.abcc`,
				`  }`,
				`}`,
			));
	});

389
	test('Accepting a member completion in word using `replace` mode should add `this.` and replace', async () => {
390
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
391

392
		const editor = await createTestEditor(testDocumentUri,
393 394 395 396 397 398 399 400
			`class Foo {`,
			`  abc = 1;`,
			`  foo() {`,
			`    ab$0c`,
			`  }`,
			`}`,
		);

401
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
402

403
		assertEditorContents(editor,
404 405 406 407 408 409 410 411 412 413
			joinLines(
				`class Foo {`,
				`  abc = 1;`,
				`  foo() {`,
				`    this.abc`,
				`  }`,
				`}`,
			));
	});

414
	test('Accepting string completion inside string using `insert` mode should insert', async () => {
415
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
416

417
		const editor = await createTestEditor(testDocumentUri,
418 419 420 421
			`const abc = { 'xy z': 123 }`,
			`abc["x$0y w"]`
		);

422
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
423

424
		assertEditorContents(editor,
425 426 427 428 429 430 431 432
			joinLines(
				`const abc = { 'xy z': 123 }`,
				`abc["xy zy w"]`
			));
	});

	// Waiting on https://github.com/microsoft/TypeScript/issues/35602
	test.skip('Accepting string completion inside string using insert mode should insert', async () => {
433
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
434

435
		const editor = await createTestEditor(testDocumentUri,
436 437 438 439
			`const abc = { 'xy z': 123 }`,
			`abc["x$0y w"]`
		);

440
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
441

442
		assertEditorContents(editor,
443 444 445 446 447
			joinLines(
				`const abc = { 'xy z': 123 }`,
				`abc["xy w"]`
			));
	});
448 449

	test('Private field completions on `this.#` should work', async () => {
M
Matt Bierner 已提交
450
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
451
			const editor = await createTestEditor(testDocumentUri,
452 453 454 455 456 457 458 459
				`class A {`,
				`  #xyz = 1;`,
				`  foo() {`,
				`    this.#$0`,
				`  }`,
				`}`,
			);

460
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
461

462
			assertEditorContents(editor,
463 464 465 466 467 468 469 470 471 472 473 474 475
				joinLines(
					`class A {`,
					`  #xyz = 1;`,
					`  foo() {`,
					`    this.#xyz`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});

	test('Private field completions on `#` should insert `this.`', async () => {
M
Matt Bierner 已提交
476
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
477
			const editor = await createTestEditor(testDocumentUri,
478 479 480 481 482 483 484 485
				`class A {`,
				`  #xyz = 1;`,
				`  foo() {`,
				`    #$0`,
				`  }`,
				`}`,
			);

486
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
487

488
			assertEditorContents(editor,
489 490 491 492 493 494 495 496 497 498 499 500 501
				joinLines(
					`class A {`,
					`  #xyz = 1;`,
					`  foo() {`,
					`    this.#xyz`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});

	test('Private field completions should not require strict prefix match (#89556)', async () => {
M
Matt Bierner 已提交
502
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
503
			const editor = await createTestEditor(testDocumentUri,
504 505 506 507 508 509 510 511
				`class A {`,
				`  #xyz = 1;`,
				`  foo() {`,
				`    this.xyz$0`,
				`  }`,
				`}`,
			);

512
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
513

514
			assertEditorContents(editor,
515 516 517 518 519 520 521 522 523 524 525 526 527
				joinLines(
					`class A {`,
					`  #xyz = 1;`,
					`  foo() {`,
					`    this.#xyz`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});

	test('Private field completions without `this.` should not require strict prefix match (#89556)', async () => {
M
Matt Bierner 已提交
528
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
529
			const editor = await createTestEditor(testDocumentUri,
530 531 532 533 534 535 536 537
				`class A {`,
				`  #xyz = 1;`,
				`  foo() {`,
				`    xyz$0`,
				`  }`,
				`}`,
			);

538
			await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
539

540
			assertEditorContents(editor,
541 542 543 544 545 546 547 548 549 550 551
				joinLines(
					`class A {`,
					`  #xyz = 1;`,
					`  foo() {`,
					`    this.#xyz`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});
552 553

	test('Accepting a completion for async property in `insert` mode should insert and add await', async () => {
554
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'insert' });
555

556
		const editor = await createTestEditor(testDocumentUri,
557 558 559 560 561 562 563 564
			`class A {`,
			`  xyz = Promise.resolve({ 'abc': 1 });`,
			`  async foo() {`,
			`    this.xyz.ab$0c`,
			`  }`,
			`}`,
		);

565
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
566

567
		assertEditorContents(editor,
568 569 570 571 572 573 574 575 576 577 578
			joinLines(
				`class A {`,
				`  xyz = Promise.resolve({ 'abc': 1 });`,
				`  async foo() {`,
				`    (await this.xyz).abcc`,
				`  }`,
				`}`,
			));
	});

	test('Accepting a completion for async property in `replace` mode should replace and add await', async () => {
579
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });
580

581
		const editor = await createTestEditor(testDocumentUri,
582 583 584 585 586 587 588 589
			`class A {`,
			`  xyz = Promise.resolve({ 'abc': 1 });`,
			`  async foo() {`,
			`    this.xyz.ab$0c`,
			`  }`,
			`}`,
		);

590
		await acceptFirstSuggestion(testDocumentUri, _disposables);
M
Matt Bierner 已提交
591

592
		assertEditorContents(editor,
593 594 595 596 597 598 599 600 601 602 603
			joinLines(
				`class A {`,
				`  xyz = Promise.resolve({ 'abc': 1 });`,
				`  async foo() {`,
				`    (await this.xyz).abc`,
				`  }`,
				`}`,
			));
	});

	test.skip('Accepting a completion for async string property should add await plus brackets', async () => {
M
Matt Bierner 已提交
604
		await enumerateConfig(testDocumentUri, Config.insertMode, insertModes, async config => {
605
			const editor = await createTestEditor(testDocumentUri,
606 607 608 609 610 611 612 613
				`class A {`,
				`  xyz = Promise.resolve({ 'ab c': 1 });`,
				`  async foo() {`,
				`    this.xyz.ab$0`,
				`  }`,
				`}`,
			);

614 615 616
			await acceptFirstSuggestion(testDocumentUri, _disposables);

			assertEditorContents(editor,
617 618 619 620 621 622 623 624 625 626 627
				joinLines(
					`class A {`,
					`  xyz = Promise.resolve({ 'abc': 1 });`,
					`  async foo() {`,
					`    (await this.xyz)["ab c"]`,
					`  }`,
					`}`,
				),
				`Config: ${config}`);
		});
	});
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

	test('Replace should work after this. (#91105)', async () => {
		await updateConfig(testDocumentUri, { [Config.insertMode]: 'replace' });

		const editor = await createTestEditor(testDocumentUri,
			`class A {`,
			`  abc = 1`,
			`  foo() {`,
			`    this.$0abc`,
			`  }`,
			`}`,
		);

		await acceptFirstSuggestion(testDocumentUri, _disposables);

		assertEditorContents(editor,
			joinLines(
				`class A {`,
				`  abc = 1`,
				`  foo() {`,
				`    this.abc`,
				`  }`,
				`}`,
			));
	});
M
Matt Bierner 已提交
653
});