From 7045bac4348802164ed6d9c9a97f8176e9d26b8e Mon Sep 17 00:00:00 2001 From: machty Date: Wed, 5 Mar 2025 15:09:39 -0500 Subject: [PATCH 1/2] start building out smoke test --- .../__tests__/support/launch-from-cli.mts | 1 + .../smoketest-ember-app-loose-and-gts.test.ts | 164 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 packages/vscode/__tests__/ts-plugin-tests/smoketest-ember-app-loose-and-gts.test.ts diff --git a/packages/vscode/__tests__/support/launch-from-cli.mts b/packages/vscode/__tests__/support/launch-from-cli.mts index eaa255439..75cfc0f38 100644 --- a/packages/vscode/__tests__/support/launch-from-cli.mts +++ b/packages/vscode/__tests__/support/launch-from-cli.mts @@ -60,6 +60,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..a4ac5db2d --- /dev/null +++ b/packages/vscode/__tests__/ts-plugin-tests/smoketest-ember-app-loose-and-gts.test.ts @@ -0,0 +1,164 @@ +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/Greeting.gts`); + + // Open the script and the template + let scriptEditor = await window.showTextDocument(scriptURI, { viewColumn: ViewColumn.One }); + + await hackishlyWaitForTypescriptPluginToActivate(scriptEditor, scriptURI); + + // Comment out a property in the script that's referenced in the template + await scriptEditor.edit((edit) => { + edit.insert(new Position(10, 4), '{{@undocumentedProperty}} '); + }); + + // Wait for a diagnostic to appear in the template + await waitUntil(() => languages.getDiagnostics(scriptURI).length); + + const fixes = await commands.executeCommand( + 'vscode.executeCodeActionProvider', + scriptURI, + new Range(new Position(10, 9), new Position(10, 9)), + ); + + expect(fixes.length).toBe(5); + + const fix = fixes.find((fix) => fix.title === "Declare property 'undocumentedProperty'"); + + expect(fix).toBeDefined(); + + // apply the missing arg fix + await workspace.applyEdit(fix!.edit!); + + await waitUntil( + () => + scriptEditor.document.getText().includes('undocumentedProperty: any') && + languages.getDiagnostics(scriptURI).length === 0, + ); + }); + }); + + // describe('diagnostics', () => { + // test('adds missing args from template into Args type', async () => { + // let scriptURI = Uri.file(`${rootDir}/app/components/Greeting.gts`); + + // // Open the script and the template + // let scriptEditor = await window.showTextDocument(scriptURI, { viewColumn: ViewColumn.One }); + + // await hackishlyWaitForTypescriptPluginToActivate(scriptEditor, scriptURI); + + // // Comment out a property in the script that's referenced in the template + // await scriptEditor.edit((edit) => { + // edit.insert(new Position(10, 4), '{{@undocumentedProperty}} '); + // }); + + // // Wait for a diagnostic to appear in the template + // await waitUntil(() => languages.getDiagnostics(scriptURI).length); + + // const fixes = await commands.executeCommand( + // 'vscode.executeCodeActionProvider', + // scriptURI, + // new Range(new Position(10, 9), new Position(10, 9)), + // ); + + // expect(fixes.length).toBe(5); + + // const fix = fixes.find((fix) => fix.title === "Declare property 'undocumentedProperty'"); + + // expect(fix).toBeDefined(); + + // // apply the missing arg fix + // await workspace.applyEdit(fix!.edit!); + + // await waitUntil( + // () => + // scriptEditor.document.getText().includes('undocumentedProperty: any') && + // languages.getDiagnostics(scriptURI).length === 0, + // ); + // }); + // }); + }); +}); + +/** + * 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'); + } +} From d58b7655900c9f151364e5b9a27fde758686792c Mon Sep 17 00:00:00 2001 From: machty Date: Wed, 5 Mar 2025 15:37:47 -0500 Subject: [PATCH 2/2] test that basic loose mode diagnostic shows up --- .../__tests__/support/launch-from-cli.mts | 10 ++- .../smoketest-ember-app-loose-and-gts.test.ts | 77 +++---------------- 2 files changed, 15 insertions(+), 72 deletions(-) diff --git a/packages/vscode/__tests__/support/launch-from-cli.mts b/packages/vscode/__tests__/support/launch-from-cli.mts index 75cfc0f38..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, 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 index a4ac5db2d..b16659662 100644 --- 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 @@ -28,83 +28,24 @@ describe('Smoke test: Loose Mode + GTS with TS Plugin Mode', () => { 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/Greeting.gts`); + 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 }); - await hackishlyWaitForTypescriptPluginToActivate(scriptEditor, scriptURI); - - // Comment out a property in the script that's referenced in the template - await scriptEditor.edit((edit) => { - edit.insert(new Position(10, 4), '{{@undocumentedProperty}} '); - }); - // Wait for a diagnostic to appear in the template await waitUntil(() => languages.getDiagnostics(scriptURI).length); - const fixes = await commands.executeCommand( - 'vscode.executeCodeActionProvider', - scriptURI, - new Range(new Position(10, 9), new Position(10, 9)), - ); - - expect(fixes.length).toBe(5); - - const fix = fixes.find((fix) => fix.title === "Declare property 'undocumentedProperty'"); - - expect(fix).toBeDefined(); - - // apply the missing arg fix - await workspace.applyEdit(fix!.edit!); - - await waitUntil( - () => - scriptEditor.document.getText().includes('undocumentedProperty: any') && - languages.getDiagnostics(scriptURI).length === 0, - ); + expect(languages.getDiagnostics(scriptURI)).toMatchObject([ + { + message: + "Property 'messageeee' does not exist on type 'ColocatedLayoutComponent'. Did you mean 'message'?", + source: 'ts-plugin', + code: 2551, + }, + ]); }); }); - - // describe('diagnostics', () => { - // test('adds missing args from template into Args type', async () => { - // let scriptURI = Uri.file(`${rootDir}/app/components/Greeting.gts`); - - // // Open the script and the template - // let scriptEditor = await window.showTextDocument(scriptURI, { viewColumn: ViewColumn.One }); - - // await hackishlyWaitForTypescriptPluginToActivate(scriptEditor, scriptURI); - - // // Comment out a property in the script that's referenced in the template - // await scriptEditor.edit((edit) => { - // edit.insert(new Position(10, 4), '{{@undocumentedProperty}} '); - // }); - - // // Wait for a diagnostic to appear in the template - // await waitUntil(() => languages.getDiagnostics(scriptURI).length); - - // const fixes = await commands.executeCommand( - // 'vscode.executeCodeActionProvider', - // scriptURI, - // new Range(new Position(10, 9), new Position(10, 9)), - // ); - - // expect(fixes.length).toBe(5); - - // const fix = fixes.find((fix) => fix.title === "Declare property 'undocumentedProperty'"); - - // expect(fix).toBeDefined(); - - // // apply the missing arg fix - // await workspace.applyEdit(fix!.edit!); - - // await waitUntil( - // () => - // scriptEditor.document.getText().includes('undocumentedProperty: any') && - // languages.getDiagnostics(scriptURI).length === 0, - // ); - // }); - // }); }); });