1import { expect, afterAll, beforeAll, it, describe } from 'vitest';
2import { TSServer } from './server';
3import path from 'node:path';
4import fs from 'node:fs';
5import url from 'node:url';
6import ts from 'typescript/lib/tsserverlibrary';
7
8const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
9
10const projectPath = path.resolve(__dirname, 'fixture-project');
11
12let server: TSServer;
13
14describe('simple', () => {
15 const testFile = path.join(projectPath, 'simple.ts');
16 const generatedFile = path.join(projectPath, 'simple.generated.ts');
17 const baseGeneratedFile = path.join(
18 projectPath,
19 '__generated__/baseGraphQLSP.ts'
20 );
21
22 beforeAll(async () => {
23 server = new TSServer(projectPath, { debugLog: false });
24 const fixtureFileContent = fs.readFileSync(
25 path.resolve(testFile, '../fixtures/simple.ts'),
26 'utf-8'
27 );
28
29 server.sendCommand('open', {
30 file: testFile,
31 fileContent: '// empty',
32 scriptKindName: 'TS',
33 } satisfies ts.server.protocol.OpenRequestArgs);
34
35 server.sendCommand('updateOpen', {
36 openFiles: [{ file: testFile, fileContent: fixtureFileContent }],
37 } satisfies ts.server.protocol.UpdateOpenRequestArgs);
38
39 server.sendCommand('saveto', {
40 file: testFile,
41 tmpfile: testFile,
42 } satisfies ts.server.protocol.SavetoRequestArgs);
43
44 await server.waitForResponse(
45 response => response.type === 'event' && response.event === 'setTypings'
46 );
47 });
48
49 afterAll(() => {
50 try {
51 fs.unlinkSync(testFile);
52 fs.unlinkSync(generatedFile);
53 fs.unlinkSync(baseGeneratedFile);
54 } catch {}
55 server.close();
56 });
57
58 it('Proposes suggestions for a selection-set', async () => {
59 server.send({
60 seq: 8,
61 type: 'request',
62 command: 'completionInfo',
63 arguments: {
64 file: testFile,
65 line: 7,
66 offset: 7,
67 includeExternalModuleExports: true,
68 includeInsertTextCompletions: true,
69 triggerKind: 1,
70 },
71 });
72
73 await server.waitForResponse(
74 response =>
75 response.type === 'response' && response.command === 'completionInfo'
76 );
77
78 const res = server.responses
79 .reverse()
80 .find(
81 resp => resp.type === 'response' && resp.command === 'completionInfo'
82 );
83
84 expect(res).toBeDefined();
85 expect(typeof res?.body.entries).toEqual('object');
86 const defaultAttrs = { kind: 'var', kindModifiers: 'declare' };
87 expect(res?.body.entries).toEqual([
88 {
89 ...defaultAttrs,
90 name: 'id',
91 sortText: '0id',
92 labelDetails: { detail: ' ID!' },
93 },
94 {
95 ...defaultAttrs,
96 name: 'content',
97 sortText: '2content',
98 labelDetails: { detail: ' String!' },
99 },
100 {
101 ...defaultAttrs,
102 name: '__typename',
103 sortText: '3__typename',
104 labelDetails: {
105 detail: ' String!',
106 description: 'The name of the current Object type at runtime.',
107 },
108 },
109 ]);
110 }, 7500);
111
112 it('Gives quick-info when hovering', async () => {
113 server.send({
114 seq: 9,
115 type: 'request',
116 command: 'quickinfo',
117 arguments: {
118 file: testFile,
119 line: 5,
120 offset: 7,
121 },
122 });
123
124 await server.waitForResponse(
125 response =>
126 response.type === 'response' && response.command === 'quickinfo'
127 );
128
129 const res = server.responses
130 .reverse()
131 .find(resp => resp.type === 'response' && resp.command === 'quickinfo');
132 expect(res).toBeDefined();
133 expect(typeof res?.body).toEqual('object');
134 expect(res?.body.documentation).toEqual(
135 `Query.posts: [Post]\n\nList out all posts`
136 );
137 }, 7500);
138});