testPackagesInEditor
packages in the editor: the lock, what uses each package, a page with its README and API, upgrading
import { plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness"; import { client } from "#codebase/tcmts/cli/test/e2e/lsp"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; export const testPackagesInEditor = () => withChildren(async (keep) => { const dir5 = tempDir("lsp packages"); const tcm = (...args: string[]) => spawnSync(process.execPath, [tcmBin(), ...args], { cwd: dir5, env: plainEnv(), encoding: "utf8" }); tcm("init"); // nothing installed: tcm provides ms fs.writeFileSync(path.join(dir5, "scratch.ts"), `// tcm: namespace time\nimport ms from "ms";\nexport const short = (n: number): string => ms(n);\n`); assert.equal(tcm("add").status, 0); spawnSync("sqlite3", [path.join(dir5, ".tcm", "codebase.sqlite"), "update packages set version = '2.1.1', integrity = null where name = 'ms'"]); const c = client(keep); await c.request("initialize", { processId: null, rootUri: pathToFileURL(dir5).href, capabilities: {}, clientInfo: { name: "Visual Studio Code" } }); c.notify("initialized", {}); const list = await c.request("tcm/packages", {}); const ms = list.find((p: { name: string }) => p.name === "ms"); assert.deepEqual(ms.ranges.map((r: { range: string; version: string; users: number; types?: string }) => [r.range, r.version, r.users, !!r.types]), [["2", "2.1.1", 1, true]]); assert.deepEqual((await c.request("tcm/packageUsers", { name: "ms", range: "2" })).map((u: { path: string }) => u.path), ["time.short"]); const page = await c.request("tcm/packagePage", { name: "ms", range: "2" }); assert.equal(page.version, "2.1.1"); assert.match(page.readme, /^# ms/m, "the package's README"); assert.ok(page.api.some((a: { name: string; kind: string }) => a.name === "default" && a.kind === "function"), "its API, from its types"); // signatures come highlighted, as a definition page's code does assert.ok(page.api.some((a: { html: string }) => /<span class="k">/.test(a.html)), "its API's signatures, highlighted"); assert.equal(page.users[0].uri, pathToFileURL(path.join(dir5, ".tcm", "src", "time", "short.ts")).href); // oxlint's findings are diagnostics in scratch files, beside tcm's own const lintFile = path.join(dir5, "lint.ts"); const lintText = `export const f = (n: number): number => {\n debugger;\n return n;\n};\n`; fs.writeFileSync(lintFile, lintText); c.notify("textDocument/didOpen", { textDocument: { uri: pathToFileURL(lintFile).href, languageId: "typescript", version: 1, text: lintText } }); const linted = await c.waitFor(() => c.notes.find((n) => n.method === "textDocument/publishDiagnostics" && n.params.uri === pathToFileURL(lintFile).href && n.params.diagnostics.some((d: { source: string }) => d.source === "oxlint")), ); const d = linted.params.diagnostics.find((x: { source: string }) => x.source === "oxlint"); assert.equal(d.code, "eslint(no-debugger)"); assert.deepEqual(d.range.start, { line: 1, character: 2 }); assert.match(d.codeDescription.href, /oxc\.rs/); // oxfmt: formatting a scratch file as it's saved, and Format Document const messy = `export const g=(n:number)=>{return n}\n`; c.notify("textDocument/didChange", { textDocument: { uri: pathToFileURL(lintFile).href, version: 2 }, contentChanges: [{ text: messy }] }); const onSave = await c.request("textDocument/willSaveWaitUntil", { textDocument: { uri: pathToFileURL(lintFile).href }, reason: 1 }); assert.equal(onSave[0].newText, "export const g = (n: number) => {\n return n;\n};\n"); assert.deepEqual(await c.request("textDocument/formatting", { textDocument: { uri: pathToFileURL(lintFile).href }, options: { tabSize: 2, insertSpaces: true } }), onSave); const upgraded = await c.request("workspace/executeCommand", { command: "tcm.upgrade", arguments: ["ms"] }); assert.equal(upgraded, true); assert.notEqual((await c.request("tcm/packages", {})).find((p: { name: string }) => p.name === "ms").ranges[0].version, "2.1.1"); c.child.kill(); })