Skip to content

Commit c58c5d9

Browse files
authored
Tests: Add more comment completion tests (#1714)
1 parent f4396f5 commit c58c5d9

File tree

2 files changed

+340
-23
lines changed

2 files changed

+340
-23
lines changed

src/editor/CommentCompletion.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ class DocCommentCompletionProvider implements vscode.CompletionItemProvider {
4545
position: vscode.Position
4646
): Promise<vscode.CompletionItem[] | undefined> {
4747
// Is line a '///' comment
48-
if (position.line === 0 || isLineComment(document, position.line - 1) === false) {
48+
if (
49+
position.line === 0 ||
50+
document.isClosed ||
51+
isLineComment(document, position.line - 1) === false
52+
) {
4953
return undefined;
5054
}
5155
await this.continueExistingDocCommentBlock(document, position);

test/integration-tests/editor/CommentCompletion.test.ts

Lines changed: 335 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -186,28 +186,6 @@ suite("CommentCompletion Test Suite", () => {
186186
]);
187187
});
188188

189-
test("Comment Insertion", async () => {
190-
const { document, positions } = await openDocument(`
191-
/// 1️⃣
192-
func foo(bar: Int, baz: String) -> Data throws { return Data() }`);
193-
const position = positions["1️⃣"];
194-
195-
const editor = await vscode.window.showTextDocument(document);
196-
await provider.insert(editor, position.line + 1);
197-
198-
assert.deepEqual(
199-
editor.document.getText(),
200-
`
201-
/// !
202-
/// !
203-
/// - Parameters:
204-
/// - bar: !
205-
/// - baz: !
206-
/// - Returns: !
207-
func foo(bar: Int, baz: String) -> Data throws { return Data() }`.replace(/!/g, "")
208-
); // ! ensures trailing white space is not trimmed when this file is formatted.
209-
});
210-
211189
test("Comment completion on function with default parameter using #function", async () => {
212190
const { document, positions } = await openDocument(`
213191
/// 1️⃣
@@ -256,9 +234,209 @@ suite("CommentCompletion Test Suite", () => {
256234
/// - Returns: $3`),
257235
]);
258236
});
237+
238+
test("Comment insertion", async () => {
239+
const { document, positions } = await openDocument(`
240+
/// 1️⃣
241+
func foo(bar: Int, baz: String) -> Data throws { return Data() }`);
242+
const position = positions["1️⃣"];
243+
244+
const editor = await vscode.window.showTextDocument(document);
245+
await provider.insert(editor, position.line + 1);
246+
247+
assert.deepEqual(
248+
editor.document.getText(),
249+
`
250+
/// !
251+
/// !
252+
/// - Parameters:
253+
/// - bar: !
254+
/// - baz: !
255+
/// - Returns: !
256+
func foo(bar: Int, baz: String) -> Data throws { return Data() }`.replace(/!/g, "")
257+
); // ! ensures trailing white space is not trimmed when this file is formatted.
258+
});
259+
260+
suite("Function Comment Completion - Edge Cases", () => {
261+
test("Comment completion on generic function", async () => {
262+
const { document, positions } = await openDocument(`
263+
/// 1️⃣
264+
func foo<T>(bar: T) -> T { return bar }`);
265+
const position = positions["1️⃣"];
266+
267+
const items = await provider.functionCommentCompletion.provideCompletionItems(
268+
document,
269+
position
270+
);
271+
assert.deepEqual(items, [
272+
expectedCompletionItem(` $1
273+
/// - Parameter bar: $2
274+
/// - Returns: $3`),
275+
]);
276+
});
277+
278+
test("Comment completion on generic function with multiple type parameters", async () => {
279+
const { document, positions } = await openDocument(`
280+
/// 1️⃣
281+
func foo<T, U>(bar: T, baz: U) -> T { return bar }`);
282+
const position = positions["1️⃣"];
283+
284+
const items = await provider.functionCommentCompletion.provideCompletionItems(
285+
document,
286+
position
287+
);
288+
assert.deepEqual(items, [
289+
expectedCompletionItem(` $1
290+
/// - Parameters:
291+
/// - bar: $2
292+
/// - baz: $3
293+
/// - Returns: $4`),
294+
]);
295+
});
296+
297+
test("Comment completion on generic function with constraints", async () => {
298+
const { document, positions } = await openDocument(`
299+
/// 1️⃣
300+
func foo<T: Equatable>(bar: T) -> T { return bar }`);
301+
const position = positions["1️⃣"];
302+
303+
const items = await provider.functionCommentCompletion.provideCompletionItems(
304+
document,
305+
position
306+
);
307+
assert.deepEqual(items, [
308+
expectedCompletionItem(` $1
309+
/// - Parameter bar: $2
310+
/// - Returns: $3`),
311+
]);
312+
});
313+
314+
test("Comment completion on malformed function - no function name", async () => {
315+
const { document, positions } = await openDocument(`
316+
/// 1️⃣
317+
func (bar: Int) {}`);
318+
const position = positions["1️⃣"];
319+
320+
const items = await provider.functionCommentCompletion.provideCompletionItems(
321+
document,
322+
position
323+
);
324+
assert.deepEqual(items, [
325+
expectedCompletionItem(` $1
326+
/// - Parameter bar: $2`),
327+
]);
328+
});
329+
330+
test("Comment completion on malformed function - no parameter name", async () => {
331+
const { document, positions } = await openDocument(`
332+
/// 1️⃣
333+
func foo(: Int) {}`);
334+
const position = positions["1️⃣"];
335+
336+
const items = await provider.functionCommentCompletion.provideCompletionItems(
337+
document,
338+
position
339+
);
340+
assert.deepEqual(items, [
341+
expectedCompletionItem(` $1
342+
/// - Parameter : $2`),
343+
]);
344+
});
345+
346+
test("Comment completion on malformed function - unclosed parameter list", async () => {
347+
const { document, positions } = await openDocument(`
348+
/// 1️⃣
349+
func foo(bar: Int`);
350+
const position = positions["1️⃣"];
351+
352+
const items = await provider.functionCommentCompletion.provideCompletionItems(
353+
document,
354+
position
355+
);
356+
assert.deepEqual(items, undefined);
357+
});
358+
359+
test("Comment completion on malformed generic function - unclosed generic list", async () => {
360+
const { document, positions } = await openDocument(`
361+
/// 1️⃣
362+
func foo<T(bar: Int) {}`);
363+
const position = positions["1️⃣"];
364+
365+
const items = await provider.functionCommentCompletion.provideCompletionItems(
366+
document,
367+
position
368+
);
369+
assert.deepEqual(items, undefined);
370+
});
371+
372+
test("Comment completion on init method", async () => {
373+
const { document, positions } = await openDocument(`
374+
/// 1️⃣
375+
init(bar: Int) {}`);
376+
const position = positions["1️⃣"];
377+
378+
const items = await provider.functionCommentCompletion.provideCompletionItems(
379+
document,
380+
position
381+
);
382+
assert.deepEqual(items, [
383+
expectedCompletionItem(` $1
384+
/// - Parameter bar: $2`),
385+
]);
386+
});
387+
388+
test("Comment completion on throwing init method", async () => {
389+
const { document, positions } = await openDocument(`
390+
/// 1️⃣
391+
init(bar: Int) throws {}`);
392+
const position = positions["1️⃣"];
393+
394+
const items = await provider.functionCommentCompletion.provideCompletionItems(
395+
document,
396+
position
397+
);
398+
assert.deepEqual(items, [
399+
expectedCompletionItem(` $1
400+
/// - Parameter bar: $2
401+
/// - Throws: $3`),
402+
]);
403+
});
404+
});
259405
});
260406

261407
suite("Document Comment Completion", () => {
408+
test("Should not provide completions on first line", async () => {
409+
const { document, positions } = await openDocument(`1️⃣
410+
public func foo() {}`);
411+
412+
const position = positions["1️⃣"];
413+
const items = await provider.docCommentCompletion.provideCompletionItems(
414+
document,
415+
position
416+
);
417+
418+
assert.strictEqual(items, undefined, "Should not provide completions on first line");
419+
});
420+
421+
test("Should not provide completions when previous line is not a comment", async () => {
422+
const { document, positions } = await openDocument(`
423+
public func bar() {}
424+
1️⃣
425+
public func foo() {}`);
426+
427+
const position = positions["1️⃣"];
428+
const items = await provider.docCommentCompletion.provideCompletionItems(
429+
document,
430+
position
431+
);
432+
433+
assert.strictEqual(
434+
items,
435+
undefined,
436+
"Should not provide completions when previous line is not a comment"
437+
);
438+
});
439+
262440
test("Should continue a documentation comment block on new line", async () => {
263441
const { document, positions } = await openDocument(`
264442
/// aaa
@@ -304,6 +482,141 @@ suite("CommentCompletion Test Suite", () => {
304482

305483
assert.deepEqual(documentText, originalText, "Document text should not change");
306484
});
485+
486+
test("Should handle case when no active text editor", async () => {
487+
const { document, positions } = await openDocument(`
488+
/// aaa
489+
1️⃣
490+
public func foo() {}`);
491+
492+
const position = positions["1️⃣"];
493+
494+
// Close all editors to simulate no active text editor
495+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
496+
497+
// This should not throw an error
498+
const items = await provider.docCommentCompletion.provideCompletionItems(
499+
document,
500+
position
501+
);
502+
503+
assert.equal(
504+
items,
505+
undefined,
506+
"Should not provide completions when no active text editor"
507+
);
508+
});
509+
510+
test("Should specifically test continueExistingDocCommentBlock with no active editor", async () => {
511+
const { document, positions } = await openDocument(`
512+
/// aaa
513+
1️⃣
514+
public func foo() {}`);
515+
516+
const position = positions["1️⃣"];
517+
518+
// Close all editors to simulate no active text editor
519+
await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS);
520+
521+
// Store original activeTextEditor property
522+
const originalActiveTextEditor = Object.getOwnPropertyDescriptor(
523+
vscode.window,
524+
"activeTextEditor"
525+
);
526+
527+
// Mock the activeTextEditor to be null for the specific method call
528+
Object.defineProperty(vscode.window, "activeTextEditor", {
529+
get: () => null,
530+
configurable: true,
531+
});
532+
533+
try {
534+
// Call the method directly to ensure the branch is covered
535+
await provider.docCommentCompletion["continueExistingDocCommentBlock"](
536+
document,
537+
position
538+
);
539+
540+
// If we get here, the method didn't throw an error, which is what we want
541+
assert.ok(true, "Method should not throw when there's no active editor");
542+
} finally {
543+
// Restore the original activeTextEditor property
544+
if (originalActiveTextEditor) {
545+
Object.defineProperty(
546+
vscode.window,
547+
"activeTextEditor",
548+
originalActiveTextEditor
549+
);
550+
}
551+
}
552+
});
553+
554+
test("Should handle when previous line has // but not ///", async () => {
555+
const { document, positions } = await openDocument(`
556+
// aaa
557+
1️⃣
558+
public func foo() {}`);
559+
560+
const position = positions["1️⃣"];
561+
const items = await provider.docCommentCompletion.provideCompletionItems(
562+
document,
563+
position
564+
);
565+
566+
assert.strictEqual(
567+
items,
568+
undefined,
569+
"Should not provide completions when previous line is not a doc comment"
570+
);
571+
});
572+
573+
test("Should handle when line has content after //", async () => {
574+
const { document, positions } = await openDocument(`
575+
/// aaa
576+
1️⃣// bbb
577+
public func foo() {}`);
578+
579+
const position = positions["1️⃣"];
580+
581+
// Show the document to ensure there's an active editor
582+
await vscode.window.showTextDocument(document);
583+
584+
const items = await provider.docCommentCompletion.provideCompletionItems(
585+
document,
586+
position
587+
);
588+
589+
// Check that the line was modified
590+
const lineText = document.lineAt(position.line).text;
591+
assert.strictEqual(lineText.trim(), "/// bbb", "Should convert // to ///");
592+
593+
assert.ok(items, "Should provide completions");
594+
assert.strictEqual(items.length, 1, "Should provide one completion");
595+
});
596+
597+
test("Should handle when line has no match for comment continuation", async () => {
598+
const { document, positions } = await openDocument(`
599+
/// aaa
600+
1️⃣let x = 1
601+
public func foo() {}`);
602+
603+
const position = positions["1️⃣"];
604+
605+
// Show the document to ensure there's an active editor
606+
await vscode.window.showTextDocument(document);
607+
608+
const originalText = document.getText();
609+
const items = await provider.docCommentCompletion.provideCompletionItems(
610+
document,
611+
position
612+
);
613+
614+
// Document should not be modified
615+
assert.strictEqual(document.getText(), originalText, "Document should not be modified");
616+
617+
assert.ok(items, "Should provide completions");
618+
assert.strictEqual(items.length, 1, "Should provide one completion");
619+
});
307620
});
308621

309622
function expectedCompletionItem(snippet: string): vscode.CompletionItem {

0 commit comments

Comments
 (0)