package ata_test import ( "context" "slices" "testing" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/bundled" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/lsp/lsproto" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/testutil/projecttestutil" "gotest.tools/v3/assert" ) func TestATA(t *testing.T) { t.Parallel() if !bundled.Embedded { t.Skip("bundled files are not embedded") } t.Run("local module should not be picked up", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": `const c = require('./config');`, "/user/username/projects/project/config.js": `export let x = 1`, "/user/username/projects/project/jsconfig.json": `{ "compilerOptions": { "moduleResolution": "commonjs" }, "typeAcquisition": { "enable": true } }`, } testOptions := &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"config"}, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, testOptions) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") content := files["/user/username/projects/project/app.js"].(string) // Open the file session.DidOpenFile(context.Background(), uri, 1, content, lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) // Verify the local config.js file is included in the program program := ls.GetProgram() assert.Assert(t, program != nil) configFile := program.GetSourceFile("/user/username/projects/project/config.js") assert.Assert(t, configFile != nil, "local config.js should be included") // Verify that only types-registry was installed (no @types/config since it's a local module) npmCalls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, len(npmCalls), 1) assert.Equal(t, npmCalls[0].Args[2], "types-registry@latest") }) t.Run("configured projects", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": ``, "/user/username/projects/project/tsconfig.json": `{ "compilerOptions": { "allowJs": true }, "typeAcquisition": { "enable": true }, }`, "/user/username/projects/project/package.json": `{ "name": "test", "dependencies": { "jquery": "^3.1.0" } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": `declare const $: { x: number }`, }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() npmCalls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, len(npmCalls), 2) assert.Equal(t, npmCalls[0].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, npmCalls[0].Args[2], "types-registry@latest") assert.Equal(t, npmCalls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Assert(t, slices.Contains(npmCalls[1].Args, "@types/jquery@latest")) assert.Equal(t, len(utils.Client().RefreshDiagnosticsCalls()), 1) }) t.Run("inferred projects", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": ``, "/user/username/projects/project/package.json": `{ "name": "test", "dependencies": { "jquery": "^3.1.0" } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": `declare const $: { x: number }`, }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called twice calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls), "Expected exactly 2 npm install calls") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) assert.Equal(t, calls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, calls[1].Args[2], "@types/jquery@latest") // Verify the types file was installed ls, err := session.GetLanguageService(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js")) assert.NilError(t, err) program := ls.GetProgram() jqueryTypesFile := program.GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts") assert.Assert(t, jqueryTypesFile != nil, "jquery types should be installed") }) t.Run("type acquisition with disableFilenameBasedTypeAcquisition:true", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/jquery.js": ``, "/user/username/projects/project/tsconfig.json": `{ "compilerOptions": { "allowJs": true }, "typeAcquisition": { "enable": true, "disableFilenameBasedTypeAcquisition": true } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"jquery"}, }) // Should only get types-registry install, no jquery install since filename-based acquisition is disabled session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/jquery.js"), 1, files["/user/username/projects/project/jquery.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called once (only types-registry) calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 1, len(calls), "Expected exactly 1 npm install call") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) }) t.Run("discover from node_modules", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{ "dependencies": { "jquery": "1.0.0" } }`, "/user/username/projects/project/jsconfig.json": `{}`, "/user/username/projects/project/node_modules/commander/index.js": "", "/user/username/projects/project/node_modules/commander/package.json": `{ "name": "commander" }`, "/user/username/projects/project/node_modules/jquery/index.js": "", "/user/username/projects/project/node_modules/jquery/package.json": `{ "name": "jquery" }`, "/user/username/projects/project/node_modules/jquery/nested/package.json": `{ "name": "nested" }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"nested", "commander"}, PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called twice calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls), "Expected exactly 2 npm install calls") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) assert.Equal(t, calls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, calls[1].Args[2], "@types/jquery@latest") }) t.Run("discover from node_modules empty types", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"dependencies": {"jquery": "1.0.0"}}`, "/user/username/projects/project/jsconfig.json": `{"compilerOptions": {"types": []}}`, "/user/username/projects/project/node_modules/commander/index.js": "", "/user/username/projects/project/node_modules/commander/package.json": `{ "name": "commander" }`, "/user/username/projects/project/node_modules/jquery/index.js": "", "/user/username/projects/project/node_modules/jquery/package.json": `{ "name": "jquery" }`, "/user/username/projects/project/node_modules/jquery/nested/package.json": `{ "name": "nested" }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"nested", "commander"}, PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Only types-registry should be installed calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 1, len(calls)) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) }) t.Run("discover from node_modules explicit types", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"dependencies": {"jquery": "1.0.0"}}`, "/user/username/projects/project/jsconfig.json": `{"compilerOptions": {"types": ["jquery"]}}`, "/user/username/projects/project/node_modules/commander/index.js": "", "/user/username/projects/project/node_modules/commander/package.json": `{ "name": "commander" }`, "/user/username/projects/project/node_modules/jquery/index.js": "", "/user/username/projects/project/node_modules/jquery/package.json": `{ "name": "jquery" }`, "/user/username/projects/project/node_modules/jquery/nested/package.json": `{ "name": "nested" }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"nested", "commander"}, PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Only types-registry should be installed calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 1, len(calls)) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) }) t.Run("discover from node_modules empty types has import", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": `import "jquery";`, "/user/username/projects/project/package.json": `{"dependencies": {"jquery": "1.0.0"}}`, "/user/username/projects/project/jsconfig.json": `{"compilerOptions": {"types": []}}`, "/user/username/projects/project/node_modules/commander/index.js": "", "/user/username/projects/project/node_modules/commander/package.json": `{ "name": "commander" }`, "/user/username/projects/project/node_modules/jquery/index.js": "", "/user/username/projects/project/node_modules/jquery/package.json": `{ "name": "jquery" }`, "/user/username/projects/project/node_modules/jquery/nested/package.json": `{ "name": "nested" }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"nested", "commander"}, PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // types-registry + jquery types calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls)) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) assert.Assert(t, slices.Contains(calls[1].Args, "@types/jquery@latest")) }) t.Run("discover from bower_components", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": ``, "/user/username/projects/project/jsconfig.json": `{}`, "/user/username/projects/project/bower_components/jquery/index.js": "", "/user/username/projects/project/bower_components/jquery/bower.json": `{ "name": "jquery" }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called twice calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls), "Expected exactly 2 npm install calls") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) assert.Equal(t, calls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, calls[1].Args[2], "@types/jquery@latest") // Verify the types file was installed ls, err := session.GetLanguageService(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js")) assert.NilError(t, err) jqueryTypesFile := ls.GetProgram().GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts") assert.Assert(t, jqueryTypesFile != nil, "jquery types should be installed") }) t.Run("discover from bower.json", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": ``, "/user/username/projects/project/jsconfig.json": `{}`, "/user/username/projects/project/bower.json": `{ "dependencies": { "jquery": "^3.1.0" } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": "declare const jquery: { x: number }", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called twice calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls), "Expected exactly 2 npm install calls") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) assert.Equal(t, calls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, calls[1].Args[2], "@types/jquery@latest") // Verify the types file was installed ls, err := session.GetLanguageService(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js")) assert.NilError(t, err) jqueryTypesFile := ls.GetProgram().GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts") assert.Assert(t, jqueryTypesFile != nil, "jquery types should be installed") }) t.Run("Malformed package.json should be watched", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"dependencies": { "co } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "commander": "export let x: number", }, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Initially only types-registry update attempted calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 1, len(calls)) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) // Fix package.json and notify watcher assert.NilError(t, utils.FS().WriteFile( "/user/username/projects/project/package.json", `{ "dependencies": { "commander": "0.0.2" } }`, false, )) session.DidChangeWatchedFiles(context.Background(), []*lsproto.FileEvent{{ Type: lsproto.FileChangeTypeChanged, Uri: lsproto.DocumentUri("file:///user/username/projects/project/package.json"), }}) // diagnostics refresh triggered - simulate by getting the language service _, _ = session.GetLanguageService(context.Background(), uri) session.WaitForBackgroundTasks() calls = utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls)) assert.Assert(t, slices.Contains(calls[1].Args, "@types/commander@latest")) // Verify types file present ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() assert.Assert(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/commander/index.d.ts") != nil) }) t.Run("should redo resolution that resolved to '.js' file after typings are installed", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": `\n import * as commander from "commander";\n `, "/user/username/projects/node_modules/commander/index.js": "module.exports = 0", } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "commander": "export let commander: number", }, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls)) assert.Assert(t, slices.Contains(calls[1].Args, "@types/commander@latest")) ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() // Types file present assert.Assert(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/commander/index.d.ts") != nil) // JS resolution should be dropped assert.Assert(t, program.GetSourceFile("/user/username/projects/node_modules/commander/index.js") == nil) }) t.Run("expired cache entry (inferred project, should install typings)", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"name":"test","dependencies":{"jquery":"^3.1.0"}}`, projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts": "export const x = 10;", projecttestutil.TestTypingsLocation + "/package.json": `{"dependencies":{"types-registry":"^0.1.317"},"devDependencies":{"@types/jquery":"^1.0.0"}}`, projecttestutil.TestTypingsLocation + "/package-lock.json": `{"dependencies":{"@types/jquery":{"version":"1.0.0"}}}`, } session, _ := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": "export const y = 10", }, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() // Expect updated content from installed typings assert.Equal(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/jquery/index.d.ts").Text(), "export const y = 10") }) t.Run("non-expired cache entry (inferred project, should not install typings)", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"name":"test","dependencies":{"jquery":"^3.1.0"}}`, projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts": "export const x = 10;", projecttestutil.TestTypingsLocation + "/package.json": `{"dependencies":{"types-registry":"^0.1.317"},"devDependencies":{"@types/jquery":"^1.3.0"}}`, projecttestutil.TestTypingsLocation + "/package-lock.json": `{"dependencies":{"@types/jquery":{"version":"1.3.0"}}}`, } session, _ := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"jquery"}, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() // Expect existing content unchanged assert.Equal(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/jquery/index.d.ts").Text(), "export const x = 10;") }) t.Run("deduplicate from local @types packages", func(t *testing.T) { t.Skip("Todo - implement removing local @types from include list") t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/node_modules/@types/node/index.d.ts": "declare var node;", "/user/username/projects/project/jsconfig.json": `{ "typeAcquisition": { "include": ["node"] } }`, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"node"}, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Only the types-registry should be installed; @types/node should NOT be installed since it exists locally npmCalls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, len(npmCalls), 1) assert.Equal(t, npmCalls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, npmCalls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) // And the program should include the local @types/node declaration file ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() assert.Assert(t, program.GetSourceFile("/user/username/projects/project/node_modules/@types/node/index.d.ts") != nil) }) t.Run("expired cache entry (inferred project, should install typings) lockfile3", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"name":"test","dependencies":{"jquery":"^3.1.0"}}`, projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts": "export const x = 10;", projecttestutil.TestTypingsLocation + "/package.json": `{"dependencies":{"types-registry":"^0.1.317"},"devDependencies":{"@types/jquery":"^1.0.0"}}`, projecttestutil.TestTypingsLocation + "/package-lock.json": `{"packages":{"node_modules/@types/jquery":{"version":"1.0.0"}}}`, } session, _ := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "jquery": "export const y = 10", }, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() // Expect updated content from installed typings assert.Equal(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/jquery/index.d.ts").Text(), "export const y = 10") }) t.Run("non-expired cache entry (inferred project, should not install typings) lockfile3", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": "", "/user/username/projects/project/package.json": `{"name":"test","dependencies":{"jquery":"^3.1.0"}}`, projecttestutil.TestTypingsLocation + "/node_modules/@types/jquery/index.d.ts": "export const x = 10;", projecttestutil.TestTypingsLocation + "/package.json": `{"dependencies":{"types-registry":"^0.1.317"},"devDependencies":{"@types/jquery":"^1.3.0"}}`, projecttestutil.TestTypingsLocation + "/package-lock.json": `{"packages":{"node_modules/@types/jquery":{"version":"1.3.0"}}}`, } session, _ := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ TypesRegistry: []string{"jquery"}, }) uri := lsproto.DocumentUri("file:///user/username/projects/project/app.js") session.DidOpenFile(context.Background(), uri, 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() ls, err := session.GetLanguageService(context.Background(), uri) assert.NilError(t, err) program := ls.GetProgram() // Expect existing content unchanged assert.Equal(t, program.GetSourceFile(projecttestutil.TestTypingsLocation+"/node_modules/@types/jquery/index.d.ts").Text(), "export const x = 10;") }) t.Run("should install typings for unresolved imports", func(t *testing.T) { t.Parallel() files := map[string]any{ "/user/username/projects/project/app.js": ` import * as fs from "fs"; import * as commander from "commander"; import * as component from "@ember/component"; `, } session, utils := projecttestutil.SetupWithTypingsInstaller(files, &projecttestutil.TypingsInstallerOptions{ PackageToFile: map[string]string{ "node": "export let node: number", "commander": "export let commander: number", "ember__component": "export let ember__component: number", }, }) session.DidOpenFile(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js"), 1, files["/user/username/projects/project/app.js"].(string), lsproto.LanguageKindJavaScript) session.WaitForBackgroundTasks() // Check that npm install was called twice calls := utils.NpmExecutor().NpmInstallCalls() assert.Equal(t, 2, len(calls), "Expected exactly 2 npm install calls") assert.Equal(t, calls[0].Cwd, projecttestutil.TestTypingsLocation) assert.DeepEqual(t, calls[0].Args, []string{"install", "--ignore-scripts", "types-registry@latest"}) // The second call should install all three packages at once assert.Equal(t, calls[1].Cwd, projecttestutil.TestTypingsLocation) assert.Equal(t, calls[1].Args[0], "install") assert.Equal(t, calls[1].Args[1], "--ignore-scripts") // Check that all three packages are in the install command installArgs := calls[1].Args assert.Assert(t, slices.Contains(installArgs, "@types/ember__component@latest")) assert.Assert(t, slices.Contains(installArgs, "@types/commander@latest")) assert.Assert(t, slices.Contains(installArgs, "@types/node@latest")) // Verify the types files were installed ls, err := session.GetLanguageService(context.Background(), lsproto.DocumentUri("file:///user/username/projects/project/app.js")) assert.NilError(t, err) program := ls.GetProgram() nodeTypesFile := program.GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/node/index.d.ts") assert.Assert(t, nodeTypesFile != nil, "node types should be installed") commanderTypesFile := program.GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/commander/index.d.ts") assert.Assert(t, commanderTypesFile != nil, "commander types should be installed") emberComponentTypesFile := program.GetSourceFile(projecttestutil.TestTypingsLocation + "/node_modules/@types/ember__component/index.d.ts") assert.Assert(t, emberComponentTypesFile != nil, "ember__component types should be installed") }) }