Skip to content

Commit 705cd4b

Browse files
committed
Rewrite completions tests
1 parent 653651a commit 705cd4b

File tree

41 files changed

+519
-315
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+519
-315
lines changed

package.json

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
},
3232
"pnpm": {
3333
"overrides": {
34+
"@volar/kit": "https://pkg.pr.new/volarjs/volar.js/@volar/kit@01441c3",
35+
"@volar/language-core": "https://pkg.pr.new/volarjs/volar.js/@volar/language-core@01441c3",
36+
"@volar/language-server": "https://pkg.pr.new/volarjs/volar.js/@volar/language-server@01441c3",
37+
"@volar/language-service": "https://pkg.pr.new/volarjs/volar.js/@volar/language-service@01441c3",
38+
"@volar/source-map": "https://pkg.pr.new/volarjs/volar.js/@volar/source-map@01441c3",
39+
"@volar/typescript": "https://pkg.pr.new/volarjs/volar.js/@volar/typescript@01441c3",
40+
"@volar/vscode": "https://pkg.pr.new/volarjs/volar.js/@volar/vscode@01441c3",
41+
"volar-service-typescript": "https://pkg.pr.new/volarjs/services/volar-service-typescript@177b9ed",
3442
"inquirer": "9.2.23"
3543
}
3644
}

packages/language-server/lib/initialize.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function initialize(
5656
};
5757
}
5858
),
59-
getFullLanguageServicePlugins(ts)
59+
getFullLanguageServicePlugins(ts, { disableAutoImportCache: params.initializationOptions.typescript.disableAutoImportCache })
6060
);
6161

6262
function updateFileWatcher(vueCompilerOptions: VueCompilerOptions) {

packages/language-server/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"dependencies": {
1818
"@volar/language-core": "~2.4.0",
1919
"@volar/language-server": "~2.4.0",
20+
"@volar/test-utils": "~2.4.0",
2021
"@vue/language-core": "2.0.28",
2122
"@vue/language-service": "2.0.28",
2223
"@vue/typescript-plugin": "2.0.28",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Completions > #2454 1`] = `
4+
"
5+
<script setup lang="ts">
6+
let vLoading: any;
7+
</script>
8+
9+
<template>
10+
<div v-loading="vLoading"></div>
11+
</template>
12+
"
13+
`;
14+
15+
exports[`Completions > #2511 1`] = `
16+
"
17+
<script setup lang="ts">
18+
import componentForAutoImport$1 from './component-for-auto-import.vue';
19+
</script>
20+
"
21+
`;
22+
23+
exports[`Completions > #3658 1`] = `
24+
"
25+
<template>
26+
<Comp>
27+
<template #foo="foo">
28+
{{ foo }}
29+
</template>
30+
</Comp>
31+
</template>
32+
"
33+
`;
34+
35+
exports[`Completions > #4639 1`] = `
36+
"
37+
<template>
38+
<div @click.capture />
39+
</template>
40+
"
41+
`;
42+
43+
exports[`Completions > $event argument 1`] = `"<template><div @click="console.log($event)"></div></template>"`;
44+
45+
exports[`Completions > <script setup> 1`] = `
46+
"
47+
<template>{{ foo }}</template>
48+
49+
<script lang="ts" setup>
50+
const foo = 1;
51+
</script>
52+
"
53+
`;
54+
55+
exports[`Completions > Alias path 1`] = `
56+
"
57+
<script setup lang="ts">
58+
import Component from '@/empty.vue';
59+
</script>
60+
"
61+
`;
62+
63+
exports[`Completions > Component auto import 1`] = `
64+
"
65+
<script setup lang="ts">
66+
import ComponentForAutoImport from './ComponentForAutoImport.vue';
67+
68+
</script>
69+
70+
<template>
71+
<ComponentForAutoImport />
72+
</template>
73+
"
74+
`;
75+
76+
exports[`Completions > Directives 1`] = `"<template><div v-html="$1"></div></template>"`;
77+
78+
exports[`Completions > Directives 2`] = `"<template><div v-cloak></div></template>"`;
79+
80+
exports[`Completions > Directives 3`] = `"<template><div v-else></div></template>"`;
81+
82+
exports[`Completions > Directives 4`] = `"<template><div v-pre></div></template>"`;
83+
84+
exports[`Completions > Relative path 1`] = `
85+
"
86+
<script setup lang="ts">
87+
import Component from './empty.vue';
88+
</script>
89+
"
90+
`;
91+
92+
exports[`Completions > Slot name 1`] = `
93+
"
94+
<template>
95+
<Foo>
96+
<template #default></template>
97+
</Foo>
98+
</template>
99+
100+
<script lang="ts" setup>
101+
let Foo: new () => {
102+
$slots: {
103+
default: any;
104+
};
105+
};
106+
</script>
107+
"
108+
`;
109+
110+
exports[`Completions > core#8811 1`] = `
111+
"
112+
<script setup lang="ts">
113+
declare const Foo: new () => {
114+
$props: {
115+
FooBar: string;
116+
};
117+
};
118+
</script>
119+
120+
<template>
121+
<Foo :-foo-bar="$1" ></Foo>
122+
</template>
123+
"
124+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import { InsertReplaceEdit, TextDocument, TextEdit } from '@volar/language-server';
2+
import { afterEach, describe, expect, it } from 'vitest';
3+
import { URI } from 'vscode-uri';
4+
import { getLanguageServer, testWorkspacePath } from './server.js';
5+
6+
describe('Completions', async () => {
7+
8+
it('Directives', async () => {
9+
await assertCompletion('fixture.vue', 'vue', `<template><div v-ht|></div></template>`, 'v-html');
10+
await assertCompletion('fixture.vue', 'vue', `<template><div v-cl|></div></template>`, 'v-cloak');
11+
await assertCompletion('fixture.vue', 'vue', `<template><div v-el|></div></template>`, 'v-else');
12+
await assertCompletion('fixture.vue', 'vue', `<template><div v-p|></div></template>`, 'v-pre');
13+
});
14+
15+
it('$event argument', async () => {
16+
await assertCompletion('fixture.vue', 'vue', `<template><div @click="console.log($eve|)"></div></template>`, '$event');
17+
});
18+
19+
it('<script setup>', async () => {
20+
await assertCompletion('fixture.vue', 'vue', `
21+
<template>{{ f| }}</template>
22+
23+
<script lang="ts" setup>
24+
const foo = 1;
25+
</script>
26+
`, 'foo');
27+
});
28+
29+
it('Slot name', async () => {
30+
await assertCompletion('fixture.vue', 'vue', `
31+
<template>
32+
<Foo>
33+
<template #|></template>
34+
</Foo>
35+
</template>
36+
37+
<script lang="ts" setup>
38+
let Foo: new () => {
39+
$slots: {
40+
default: any;
41+
};
42+
};
43+
</script>
44+
`, 'default');
45+
});
46+
47+
it('#2454', async () => {
48+
await assertCompletion('fixture.vue', 'vue', `
49+
<script setup lang="ts">
50+
let vLoading: any;
51+
</script>
52+
53+
<template>
54+
<div v-load|="vLoading"></div>
55+
</template>
56+
`, 'v-loading');
57+
});
58+
59+
it('#2511', async () => {
60+
await ensureGlobalTypesHolder('tsconfigProject');
61+
await openDocument('tsconfigProject/component-for-auto-import.vue', 'vue', `<script setup lang="ts"></script>`);
62+
await assertCompletion('tsconfigProject/fixture.vue', 'vue', `
63+
<script setup lang="ts">
64+
import componentFor|
65+
</script>
66+
`, 'ComponentForAutoImport');
67+
});
68+
69+
it('#3658', async () => {
70+
await assertCompletion('fixture.vue', 'vue', `
71+
<template>
72+
<Comp>
73+
<template #foo="foo">
74+
{{ fo| }}
75+
</template>
76+
</Comp>
77+
</template>
78+
`, 'foo');
79+
});
80+
81+
it('#4639', async () => {
82+
await assertCompletion('fixture.vue', 'vue', `
83+
<template>
84+
<div @click.| />
85+
</template>
86+
`, 'capture');
87+
});
88+
89+
it('Alias path', async () => {
90+
await ensureGlobalTypesHolder('tsconfigProject');
91+
await assertCompletion('tsconfigProject/fixture.vue', 'vue', `
92+
<script setup lang="ts">
93+
import Component from '@/|';
94+
</script>
95+
`, 'empty.vue');
96+
});
97+
98+
it('Relative path', async () => {
99+
await ensureGlobalTypesHolder('tsconfigProject');
100+
await assertCompletion('tsconfigProject/fixture.vue', 'vue', `
101+
<script setup lang="ts">
102+
import Component from './|';
103+
</script>
104+
`, 'empty.vue');
105+
});
106+
107+
it('Component auto import', async () => {
108+
await ensureGlobalTypesHolder('tsconfigProject');
109+
await openDocument('tsconfigProject/ComponentForAutoImport.vue', 'vue', `<script setup lang="ts"></script>`);
110+
await assertCompletion('tsconfigProject/fixture.vue', 'vue', `
111+
<script setup lang="ts">
112+
</script>
113+
114+
<template>
115+
<ComponentForA| />
116+
</template>
117+
`, 'ComponentForAutoImport');
118+
});
119+
120+
it('core#8811', async () => {
121+
await ensureGlobalTypesHolder('tsconfigProject');
122+
await assertCompletion('tsconfigProject/fixture.vue', 'vue', `
123+
<script setup lang="ts">
124+
declare const Foo: new () => {
125+
$props: {
126+
FooBar: string;
127+
};
128+
};
129+
</script>
130+
131+
<template>
132+
<Foo :-| ></Foo>
133+
</template>
134+
`, ':-foo-bar');
135+
});
136+
137+
const openedDocuments: TextDocument[] = [];
138+
139+
afterEach(async () => {
140+
const server = await getLanguageServer();
141+
for (const document of openedDocuments) {
142+
await server.closeTextDocument(document.uri);
143+
}
144+
openedDocuments.length = 0;
145+
});
146+
147+
/**
148+
* @deprecated Remove this when #4717 fixed.
149+
*/
150+
async function ensureGlobalTypesHolder(folderName: string) {
151+
const document = await openDocument(`${folderName}/globalTypesHolder.vue`, 'vue', '');
152+
const server = await getLanguageServer();
153+
await server.sendDocumentDiagnosticRequest(document.uri);
154+
}
155+
156+
async function assertCompletion(fileName: string, languageId: string, content: string, itemLabel: string) {
157+
const offset = content.indexOf('|');
158+
content = content.slice(0, offset) + content.slice(offset + 1);
159+
160+
const server = await getLanguageServer();
161+
let document = await openDocument(fileName, languageId, content);
162+
163+
const position = document.positionAt(offset);
164+
const completions = await server.sendCompletionRequest(document.uri, position);
165+
166+
let completion = completions?.items.find(item => item.label === itemLabel);
167+
expect(completion).toBeDefined();
168+
169+
completion = await server.sendCompletionResolveRequest(completion!);
170+
expect(completion).toBeDefined();
171+
172+
const edits: TextEdit[] = [];
173+
174+
if (completion.textEdit) {
175+
if (InsertReplaceEdit.is(completion.textEdit)) {
176+
edits.push({ newText: completion.textEdit.newText, range: completion.textEdit.replace });
177+
}
178+
else {
179+
edits.push(completion.textEdit);
180+
}
181+
}
182+
else {
183+
edits.push({
184+
newText: completion.insertText ?? completion.label,
185+
range: { start: position, end: position },
186+
});
187+
}
188+
if (completion.additionalTextEdits) {
189+
edits.push(...completion.additionalTextEdits);
190+
}
191+
if (edits.length === 0) {
192+
console.log(completion);
193+
}
194+
expect(edits.length).toBeGreaterThan(0);
195+
196+
document = await server.updateTextDocument(document.uri, edits);
197+
198+
expect(document.getText()).toMatchSnapshot();
199+
}
200+
201+
async function openDocument(fileName: string, languageId: string, content: string) {
202+
const server = await getLanguageServer();
203+
const uri = URI.file(`${testWorkspacePath}/${fileName}`);
204+
const document = await server.openInMemoryDocument(uri.toString(), languageId, content);
205+
if (openedDocuments.every(d => d.uri !== document.uri)) {
206+
openedDocuments.push(document);
207+
}
208+
return document;
209+
}
210+
});
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as path from 'path';
2+
import type { LanguageServerHandle } from '@volar/test-utils';
3+
import { startLanguageServer } from '@volar/test-utils';
4+
import { URI } from 'vscode-uri';
5+
6+
let serverHandle: LanguageServerHandle | undefined;
7+
8+
export const testWorkspacePath = path.resolve(__dirname, '../../../test-workspace');
9+
10+
export async function getLanguageServer() {
11+
if (!serverHandle) {
12+
serverHandle = startLanguageServer(require.resolve('../bin/vue-language-server.js'), testWorkspacePath);
13+
serverHandle.connection.onNotification('textDocument/publishDiagnostics', () => { });
14+
15+
await serverHandle.initialize(
16+
URI.file(testWorkspacePath).toString(),
17+
{
18+
typescript: {
19+
tsdk: path.dirname(require.resolve('typescript/lib/typescript.js')),
20+
disableAutoImportCache: true,
21+
},
22+
vue: {
23+
hybridMode: false,
24+
},
25+
}
26+
);
27+
}
28+
return serverHandle;
29+
}

0 commit comments

Comments
 (0)