diff --git a/packages/vscode/__tests__/support/launch-from-cli.mts b/packages/vscode/__tests__/support/launch-from-cli.mts index eaa255439..36c04cec1 100644 --- a/packages/vscode/__tests__/support/launch-from-cli.mts +++ b/packages/vscode/__tests__/support/launch-from-cli.mts @@ -10,15 +10,13 @@ const emptyUserDataDir = path.join(os.tmpdir(), `user-data-${Math.random()}`); const settingsDir = path.join(emptyUserDataDir, 'User'); fs.mkdirSync(settingsDir, { recursive: true }); -const userPreferences = { +const userPreferences: Record = { // When testing TS Plugin, it can be useful to look at tsserver logs within // the test runner VSCode instance. To do this, uncomment the following line, // and then check the logs for TypeScript - // "typescript.tsserver.log": "verbose", + // 'typescript.tsserver.log': 'verbose', }; -fs.writeFileSync(path.join(settingsDir, 'settings.json'), JSON.stringify(userPreferences, null, 2)); - const testType = process.argv[2]; let disableExtensionArgs: string[] = []; @@ -34,6 +32,8 @@ switch (testType) { case 'ts-plugin': testRunner = 'lib/__tests__/support/vscode-runner-ts-plugin.js'; + userPreferences['glint.server.hybridMode'] = true; + // Note: here, we WANT vanilla TS to be enabled since we're testing the TS Plugin. break; default: @@ -41,6 +41,8 @@ switch (testType) { process.exit(1); } +fs.writeFileSync(path.join(settingsDir, 'settings.json'), JSON.stringify(userPreferences, null, 2)); + try { runTests({ extensionDevelopmentPath: packageRoot, @@ -60,6 +62,7 @@ try { `${packageRoot}/__fixtures__/ember-app`, `${packageRoot}/__fixtures__/template-imports-app`, `${packageRoot}/__fixtures__/template-imports-app-ts-plugin`, + `${packageRoot}/__fixtures__/ember-app-loose-and-gts`, ], }); } catch (error) { diff --git a/packages/vscode/__tests__/ts-plugin-tests/smoketest-ember-app-loose-and-gts.test.ts b/packages/vscode/__tests__/ts-plugin-tests/smoketest-ember-app-loose-and-gts.test.ts new file mode 100644 index 000000000..b16659662 --- /dev/null +++ b/packages/vscode/__tests__/ts-plugin-tests/smoketest-ember-app-loose-and-gts.test.ts @@ -0,0 +1,105 @@ +import { + commands, + languages, + ViewColumn, + window, + Uri, + Range, + Position, + CodeAction, + workspace, + TextEditor, +} from 'vscode'; +import * as path from 'path'; +import { describe, afterEach, test } from 'mocha'; +import { expect } from 'expect'; +import { waitUntil } from '../helpers/async'; + +describe('Smoke test: Loose Mode + GTS with TS Plugin Mode', () => { + const rootDir = path.resolve(__dirname, '../../../__fixtures__/ember-app-loose-and-gts'); + + afterEach(async () => { + while (window.activeTextEditor) { + await commands.executeCommand('workbench.action.files.revert'); + await commands.executeCommand('workbench.action.closeActiveEditor'); + } + }); + + describe.only('loose mode aka ts + hbs two-file components', () => { + describe('diagnostics', () => { + test('adds missing args from template into Args type', async () => { + let scriptURI = Uri.file(`${rootDir}/app/components/colocated-layout-with-errors.hbs`); + + // Open the script and the template + let scriptEditor = await window.showTextDocument(scriptURI, { viewColumn: ViewColumn.One }); + + // Wait for a diagnostic to appear in the template + await waitUntil(() => languages.getDiagnostics(scriptURI).length); + + expect(languages.getDiagnostics(scriptURI)).toMatchObject([ + { + message: + "Property 'messageeee' does not exist on type 'ColocatedLayoutComponent'. Did you mean 'message'?", + source: 'ts-plugin', + code: 2551, + }, + ]); + }); + }); + }); +}); + +/** + * It takes a little while for the TS Plugin to fully activate, and unfortunately + * VSCode won't automatically re-trigger/re-calculate diagnostics for a file after + * a TS Plugin kicks in, so we need some way to know that the TS Plugin is activated + * before we edit the file. + * + * To accomplish this, this function inserts invalid TS into the .gts file and waits + * for diagnostics to show up. + */ +async function hackishlyWaitForTypescriptPluginToActivate( + scriptEditor: TextEditor, + scriptURI: Uri, +): Promise { + let invalidAssignment = 'let s: string = 123;'; + await scriptEditor.edit((edit) => { + edit.insert(new Position(0, 0), invalidAssignment); + }); + + let numSpacesAdded = 0; + const startTime = Date.now(); + + // eslint-disable-next-line no-constant-condition + while (true) { + await scriptEditor.edit((edit) => { + edit.insert(new Position(0, 0), ' '); + }); + numSpacesAdded++; + + if (languages.getDiagnostics(scriptURI).length) { + break; + } + + if (Date.now() - startTime > 5000) { + throw new Error( + 'Timed out waiting for TS Plugin to activate (i.e. waiting for diagnostics to show up)', + ); + } + + // We'd love to wait for a smaller increment than 1000 but the editor + // debounces before triggering diagnostics so we need a large enough time. + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + // Remove our invalid assignment + await scriptEditor.edit((edit) => { + edit.replace(new Range(0, 0, 0, invalidAssignment.length + numSpacesAdded), ''); + }); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + if (languages.getDiagnostics(scriptURI).length) { + throw new Error('Diagnostics still showing up after removing invalid assignment'); + } +}