testPages
pages: a definition's rendered doc and linked source, a namespace's README, and the finder
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 testPages = () => withChildren(async (keep) => { const dir4 = tempDir("lsp4"); const tcm = (...args: string[]) => spawnSync(process.execPath, [tcmBin(), ...args], { cwd: dir4, env: plainEnv(), encoding: "utf8" }); tcm("init"); fs.writeFileSync( path.join(dir4, "scratch.ts"), `// tcm: namespace geo /** A point on the plane. */ export type Point = { x: number; y: number }; /** * Distance between two {@link Point}s. * * \`\`\`ts * dist({ x: 0, y: 0 }, { x: 3, y: 4 }) * \`\`\` */ export const dist = (a: Point, b: Point): number => Math.hypot(a.x - b.x, a.y - b.y); /** Around the outside: the sum of each side's {@link dist}. */ export const perimeter = (ps: Point[]): number => ps.reduce((n, p, i) => n + dist(p, ps[(i + 1) % ps.length]!), 0); /** * # Geometry * * Points and distances. Start with {@link dist}. */ export type README = never; `, ); assert.equal(tcm("add").status, 0); // and a page, kept as written, linking to code fs.writeFileSync(path.join(dir4, "guide.md"), "<!-- tcm: namespace geo -->\n# Guide\n\nMeasure with {@link dist}.\n"); assert.equal(tcm("add", "guide.md").status, 0); const dist = spawnSync("sqlite3", [path.join(dir4, ".tcm", "codebase.sqlite"), "select hash from names where path = 'geo.dist'"], { encoding: "utf8" }).stdout.trim(); const pointHash = spawnSync("sqlite3", [path.join(dir4, ".tcm", "codebase.sqlite"), "select hash from names where path = 'geo.Point'"], { encoding: "utf8" }).stdout.trim(); const c = client(keep); await c.request("initialize", { processId: null, rootUri: pathToFileURL(dir4).href, capabilities: {}, clientInfo: { name: "Visual Studio Code" } }); c.notify("initialized", {}); // a definition's page: rendered doc (links by hash, the example evaluated), source with references as links const page = await c.request("tcm/page", { path: "geo.dist" }); assert.equal(page.hash, dist); assert.deepEqual(page.names, ["geo.dist"]); assert.match(page.docMarkdown, new RegExp(`Distance between two \\[\`Point\`\\]\\(tcm-page:${pointHash}\\)s\\.`)); assert.match(page.docMarkdown, /```text\n⧩ 5\n```/); assert.match(page.sourceHtml, new RegExp(`<a class="ref t" data-hash="${pointHash}">Point</a>`)); assert.match(page.sourceHtml, /<span class="k">const<\/span>/); // its imports link too: what each brings in, and the namespace it's from assert.match(page.sourceHtml, /<a class="ref s" data-namespace="geo">"#codebase\/geo"<\/a>/); assert.equal(page.sourceHtml.split(`data-hash="${pointHash}">Point</a>`).length - 1, 3, "Point in the import, and in both parameters"); assert.deepEqual(page.uses.map((u: any) => u.name), ["geo.Point"]); // (the page linking to it uses it too) assert.deepEqual(page.usedBy.map((u: any) => u.name), ["geo.guide", "geo.perimeter"]); // files are in the tree, with pages of their own: Markdown rendered, its links to code opening their pages const tree = await c.request("tcm/tree", { namespace: "geo" }); assert.ok(tree.definitions.some((d: any) => d.name === "guide" && d.kind === "file" && d.role === "file"), JSON.stringify(tree.definitions)); const guide = await c.request("tcm/page", { path: "geo.guide" }); assert.equal(guide.kind, "file"); assert.equal(guide.format, "md"); assert.match(guide.docMarkdown, new RegExp(`Measure with \\[\`geo\\.dist\`\\]\\(tcm-page:${dist}\\)`)); // a namespace's page: its README rendered, then what's in it by role const ns = await c.request("tcm/namespace", { namespace: "geo" }); assert.match(ns.readme, /^# Geometry\n\nPoints and distances\. Start with \[`dist`\]\(tcm-page:/); assert.deepEqual(ns.definitions.map((d: any) => [d.name, d.role]), [["Point", "type"], ["dist", "shared"], ["guide", "file"], ["perimeter", "shared"]]); assert.equal(ns.definitions[0].summary, "A point on the plane."); // the finder: names (best first), words in docs, and types const byName = await c.request("tcm/find", { query: "dist" }); assert.equal(byName[0].path, "geo.dist"); const byDoc = await c.request("tcm/find", { query: "outside" }); assert.deepEqual(byDoc.map((i: any) => [i.path, i.match]), [["geo.perimeter", "doc"]]); const byType = await c.request("tcm/find", { query: "(a: Point, b: Point) => number" }); assert.deepEqual(byType.map((i: any) => [i.path, i.match]), [["geo.dist", "type"]]); })