Skip to content

Commit ce840d8

Browse files
committed
test: add LSP integration tests for TypeScriptSupport (JS + TS)
Cover the desktop LSP end to end against the real vtsls server: - a .ts file reports a real type error, - a JS project that opts into checkJs (jsconfig) shows implicit-any (kept), - a plain JS project does NOT show implicit-any (suppressed) - with a checkJs precondition so the negative assertion reflects gating, not timing. The tests live in the extension's unittests.js (previously an empty shim) and follow the standard createTestWindowAndRun + brackets.test.* integration pattern; they require core modules via brackets.getModule and are gated to the desktop build (Phoenix.isNativeApp), skipped in the browser. main.js sets a _TypeScriptSupportReadyToIntegTest flag so the tests can await server startup. Fixtures are tiny and need no node_modules. Verified 3/3 passing.
1 parent 28f37a8 commit ce840d8

6 files changed

Lines changed: 141 additions & 0 deletions

File tree

src/extensions/default/TypeScriptSupport/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ define(function (require, exports, module) {
264264
_refreshCheckJs();
265265
start().catch(function (err) {
266266
console.error("[TypeScriptSupport] init failed", err && (err.message || err));
267+
}).finally(function () {
268+
// Signal for integration tests that the server start has been attempted/settled.
269+
window._TypeScriptSupportReadyToIntegTest = true;
267270
});
268271

269272
// Restart the server against the new workspace root when the project changes, and
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it
7+
* under the terms of the GNU Affero General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
14+
* for more details.
15+
*
16+
* You should have received a copy of the GNU Affero General Public License
17+
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
18+
*
19+
*/
20+
21+
/*global describe, it, expect, beforeAll, afterAll, awaitsFor, awaitsForDone */
22+
23+
define(function (require, exports, module) {
24+
25+
const SpecRunnerUtils = brackets.getModule("spec/SpecRunnerUtils");
26+
27+
const IMPLICIT_ANY_MESSAGE = "implicitly has an 'any' type";
28+
29+
describe("integration:TypeScript LSP", function () {
30+
const testRootSpec = "/spec/TypeScriptSupport-test-files/";
31+
let testFolder = SpecRunnerUtils.getTestPath(testRootSpec),
32+
testWindow,
33+
$,
34+
EditorManager,
35+
CommandManager,
36+
Commands,
37+
CodeInspection;
38+
39+
// The LSP runs only in the desktop app (it spawns the vtsls Node process), so these tests
40+
// are meaningless in the browser build - register a single skipped placeholder and bail.
41+
if (!Phoenix.isNativeApp) {
42+
it("is desktop-only - skipped in the browser build", function () {
43+
expect(Phoenix.isNativeApp).toBeFalsy();
44+
});
45+
return;
46+
}
47+
48+
beforeAll(async function () {
49+
testWindow = await SpecRunnerUtils.createTestWindowAndRun();
50+
$ = testWindow.$;
51+
EditorManager = testWindow.brackets.test.EditorManager;
52+
CommandManager = testWindow.brackets.test.CommandManager;
53+
Commands = testWindow.brackets.test.Commands;
54+
CodeInspection = testWindow.brackets.test.CodeInspection;
55+
CodeInspection.toggleEnabled(true);
56+
// Wait until the extension has attempted to start the language server.
57+
await awaitsFor(function () {
58+
return testWindow._TypeScriptSupportReadyToIntegTest;
59+
}, "TypeScript LSP server to start", 30000);
60+
}, 30000);
61+
62+
afterAll(async function () {
63+
testWindow = null;
64+
$ = null;
65+
EditorManager = null;
66+
CommandManager = null;
67+
Commands = null;
68+
CodeInspection = null;
69+
await SpecRunnerUtils.closeTestWindow();
70+
}, 30000);
71+
72+
function panelText() {
73+
return $("#problems-panel").text();
74+
}
75+
76+
async function _openInProject(subFolder, fileName) {
77+
await SpecRunnerUtils.loadProjectInTestWindow(testFolder + subFolder);
78+
await awaitsForDone(SpecRunnerUtils.openProjectFiles([fileName]), "open " + fileName);
79+
}
80+
81+
it("should report TypeScript type errors from the language server", async function () {
82+
await _openInProject("ts/", "type-error.ts");
83+
// type-error.ts assigns a string to a `number` -> TS2322 "... not assignable ...".
84+
await awaitsFor(function () {
85+
return panelText().includes("not assignable");
86+
}, "TypeScript type error to be reported", 20000);
87+
}, 30000);
88+
89+
it("should report implicit-any in a JS project that opts into checkJs", async function () {
90+
// js-checkjs has a jsconfig.json with checkJs + noImplicitAny, so the untyped parameter
91+
// in implicit.js IS flagged - and our diagnostic filter keeps it (the project opted in).
92+
await _openInProject("js-checkjs/", "implicit.js");
93+
await awaitsFor(function () {
94+
return panelText().includes(IMPLICIT_ANY_MESSAGE);
95+
}, "implicit-any to be reported under checkJs", 20000);
96+
}, 30000);
97+
98+
it("should NOT report implicit-any in a plain JS project", async function () {
99+
// Precondition: confirm the server actually produces implicit-any for this exact code
100+
// (under checkJs), so the plain-project assertion below reflects gating, not just timing.
101+
await _openInProject("js-checkjs/", "implicit.js");
102+
await awaitsFor(function () {
103+
return panelText().includes(IMPLICIT_ANY_MESSAGE);
104+
}, "implicit-any under checkJs (precondition)", 20000);
105+
106+
// Same code in a plain JS project (no jsconfig / no @ts-check): the "go add types" nag
107+
// must not appear. Wait for inspection to settle clean, then assert it is absent.
108+
await _openInProject("js-plain/", "implicit.js");
109+
await awaitsFor(function () {
110+
return $("#status-inspection").hasClass("inspection-valid");
111+
}, "plain JS inspection to settle with no problems", 20000);
112+
expect(panelText().includes(IMPLICIT_ANY_MESSAGE)).toBe(false);
113+
}, 30000);
114+
});
115+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Same untyped parameter as js-plain, but this project opts into type-checking via jsconfig
2+
// (checkJs + noImplicitAny), so the LSP SHOULD report the implicit "any".
3+
export function identity(value) {
4+
return value;
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"checkJs": true,
4+
"noImplicitAny": true
5+
},
6+
"include": ["**/*.js"]
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Plain JavaScript (no jsconfig/tsconfig, no @ts-check). The untyped parameter would be an
2+
// "implicit any" under type-checking, but a pure-JS project must NOT be nagged about it.
3+
export function identity(value) {
4+
return value;
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// A deliberate TypeScript type error for the LSP integration test.
2+
const aNumber: number = "this is a string";
3+
4+
export function getValue(): number {
5+
return aNumber;
6+
}

0 commit comments

Comments
 (0)