test(language-service): Add tests for quickinfo and definition (#29990)
`quickinfo` is used for hover tooltip. `definition` is used for "Go to definition". PR Close #29990
This commit is contained in:
parent
f348deae92
commit
017bf0b794
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"seq": 0,
|
||||
"type": "response",
|
||||
"command": "definition",
|
||||
"request_seq": 2,
|
||||
"success": true,
|
||||
"body": [
|
||||
{
|
||||
"file": "${PWD}/project/app/app.component.ts",
|
||||
"start": {
|
||||
"line": 7,
|
||||
"offset": 30
|
||||
},
|
||||
"end": {
|
||||
"line": 7,
|
||||
"offset": 47
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"seq": 0,
|
||||
"type": "response",
|
||||
"command": "quickinfo",
|
||||
"request_seq": 2,
|
||||
"success": true,
|
||||
"body": {
|
||||
"kind": "angular",
|
||||
"kindModifiers": "what does this do?",
|
||||
"start": {
|
||||
"line": 5,
|
||||
"offset": 26
|
||||
},
|
||||
"end": {
|
||||
"line": 5,
|
||||
"offset": 30
|
||||
},
|
||||
"displayString": "property name of AppComponent",
|
||||
"documentation": "",
|
||||
"tags": []
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { writeFileSync } from 'fs';
|
||||
import { writeFileSync, readFileSync } from 'fs';
|
||||
|
||||
const goldens: string[] = process.argv.slice(2);
|
||||
|
||||
|
@ -6,16 +6,18 @@ export const goldenMatcher: jasmine.CustomMatcherFactories = {
|
|||
toMatchGolden(util: jasmine.MatchersUtil): jasmine.CustomMatcher {
|
||||
return {
|
||||
compare(actual: {command: string}, golden: string): jasmine.CustomMatcherResult {
|
||||
const expected = require(`./goldens/${golden}`);
|
||||
const pass = util.equals(actual, expected);
|
||||
if (!pass && goldens.indexOf(golden) >= 0) {
|
||||
if (goldens.includes(golden)) {
|
||||
console.error(`Writing golden file ${golden}`);
|
||||
writeFileSync(`./goldens/${golden}`, JSON.stringify(actual, null, 2));
|
||||
return { pass : true };
|
||||
}
|
||||
const content = readFileSync(`./goldens/${golden}`, 'utf-8');
|
||||
const expected = JSON.parse(content.replace("${PWD}", process.env.PWD!));
|
||||
const pass = util.equals(actual, expected);
|
||||
return {
|
||||
pass,
|
||||
message: `Expected response for '${actual.command}' to match golden file ${golden}.\n` +
|
||||
message: `Expected ${JSON.stringify(actual, null, 2)} to match golden ` +
|
||||
`${JSON.stringify(expected, null, 2)}.\n` +
|
||||
`To generate new golden file, run "yarn golden ${golden}".`,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"cleanup": "rm -rf ti-*.log tsserver.log",
|
||||
"golden": "node generate.js",
|
||||
"golden": "yarn build && node generate.js",
|
||||
"test": "yarn cleanup && yarn build && jasmine test.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ describe('Angular Language Service', () => {
|
|||
// https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1055
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/app.module.ts`,
|
||||
fileContent: ""
|
||||
});
|
||||
// Server does not send response to geterr request
|
||||
// https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1770
|
||||
|
@ -77,7 +76,6 @@ describe('Angular Language Service', () => {
|
|||
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
fileContent: "import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n template: `<h1>Hello {{name}}</h1>`,\n})\nexport class AppComponent { name = 'Angular'; }\n"
|
||||
});
|
||||
|
||||
client.sendRequest('geterr', {
|
||||
|
@ -101,4 +99,44 @@ describe('Angular Language Service', () => {
|
|||
});
|
||||
expect(response).toMatchGolden('completionInfo.json');
|
||||
});
|
||||
|
||||
it('should perform quickinfo', async () => {
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
});
|
||||
|
||||
const resp1 = await client.sendRequest('reload', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
tmpFile: `${PWD}/project/app/app.component.ts`,
|
||||
}) as any;
|
||||
expect(resp1.command).toBe('reload');
|
||||
expect(resp1.success).toBe(true);
|
||||
|
||||
const resp2 = await client.sendRequest('quickinfo', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
line: 5,
|
||||
offset: 28,
|
||||
});
|
||||
expect(resp2).toMatchGolden('quickinfo.json');
|
||||
});
|
||||
|
||||
it('should perform definition', async () => {
|
||||
client.sendRequest('open', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
});
|
||||
|
||||
const resp1 = await client.sendRequest('reload', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
tmpFile: `${PWD}/project/app/app.component.ts`,
|
||||
}) as any;
|
||||
expect(resp1.command).toBe('reload');
|
||||
expect(resp1.success).toBe(true);
|
||||
|
||||
const resp2 = await client.sendRequest('definition', {
|
||||
file: `${PWD}/project/app/app.component.ts`,
|
||||
line: 5,
|
||||
offset: 28,
|
||||
});
|
||||
expect(resp2).toMatchGolden('definition.json');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,51 +15,56 @@ export class Client {
|
|||
listen() {
|
||||
this.server.stdout.on('data', (data: Buffer) => {
|
||||
this.data = this.data ? Buffer.concat([this.data, data]) : data;
|
||||
// tsserver could batch multiple responses together so we have to go
|
||||
// through the entire buffer to keep looking for messages.
|
||||
const CONTENT_LENGTH = 'Content-Length: '
|
||||
const index = this.data.indexOf(CONTENT_LENGTH);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
let start = index + CONTENT_LENGTH.length;
|
||||
let end = this.data.indexOf('\r\n', start);
|
||||
if (end < start) {
|
||||
return;
|
||||
}
|
||||
const contentLengthStr = this.data.slice(start, end).toString();
|
||||
const contentLength = Number(contentLengthStr);
|
||||
if (isNaN(contentLength) || contentLength < 0) {
|
||||
return;
|
||||
}
|
||||
start = end + 4;
|
||||
end = start + contentLength;
|
||||
if (end > this.data.length) {
|
||||
return;
|
||||
}
|
||||
const content = this.data.slice(start, end).toString();
|
||||
this.data = this.data.slice(end);
|
||||
try {
|
||||
const payload = JSON.parse(content);
|
||||
if (payload.type === "event") {
|
||||
do {
|
||||
const index = this.data.indexOf(CONTENT_LENGTH);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
this.responseEmitter.emit('response', payload);
|
||||
}
|
||||
catch (error) {
|
||||
this.responseEmitter.emit('error', error);
|
||||
}
|
||||
let start = index + CONTENT_LENGTH.length;
|
||||
let end = this.data.indexOf('\r\n', start);
|
||||
if (end < start) {
|
||||
return;
|
||||
}
|
||||
const contentLengthStr = this.data.slice(start, end).toString();
|
||||
const contentLength = Number(contentLengthStr);
|
||||
if (isNaN(contentLength) || contentLength < 0) {
|
||||
return;
|
||||
}
|
||||
start = end + 4;
|
||||
end = start + contentLength;
|
||||
if (end > this.data.length) {
|
||||
return;
|
||||
}
|
||||
const content = this.data.slice(start, end).toString();
|
||||
this.data = this.data.slice(end);
|
||||
try {
|
||||
const payload = JSON.parse(content);
|
||||
if (payload.type === "response") {
|
||||
const seq = `${payload.request_seq}`;
|
||||
this.responseEmitter.emit(seq, payload);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
this.responseEmitter.emit('error', error);
|
||||
}
|
||||
} while (this.data.length > 0)
|
||||
});
|
||||
}
|
||||
|
||||
async send(type: string, command: string, params: {}) {
|
||||
const seq = this.id++;
|
||||
const request = {
|
||||
seq: this.id++,
|
||||
seq,
|
||||
type,
|
||||
command,
|
||||
arguments: params
|
||||
};
|
||||
this.server.stdin.write(JSON.stringify(request) + '\r\n');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.responseEmitter.once('response', resolve);
|
||||
this.responseEmitter.once(`${seq}`, resolve);
|
||||
this.responseEmitter.once('error', reject);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
"target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||
"target": "es2016", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
|
|
Loading…
Reference in New Issue