test(language-server): cover didClose region-cache clear end-to-end
Replaces the direct-call-only test with an over-stdio test that exercises server.ts's didClose handler itself, so it fails if the clearHtmlRegionCache wiring is removed or misparameterized.
This commit is contained in:
@@ -217,3 +217,91 @@ test("coalesces rapid document changes into one pending diagnostic analysis", as
|
||||
process.kill();
|
||||
await process.exited;
|
||||
});
|
||||
|
||||
test("didClose drops the region cache so a reopened document at the same version is rescanned", async () => {
|
||||
// Regression coverage for the didClose wiring in server.ts: without the
|
||||
// clearHtmlRegionCache(uri) call there, a document that closes and reopens
|
||||
// at version 1 (a common restart point) matches the stale cache entry from
|
||||
// the prior session. Open first WITHOUT a view block (caching "no HTML
|
||||
// here" for this uri/version), close, then reopen the SAME uri at the SAME
|
||||
// version WITH a view block covering the same offset: correct behaviour
|
||||
// rescans and finds it, a stale cache still says "no HTML here" and
|
||||
// suppresses the completions entirely.
|
||||
const process = Bun.spawn(
|
||||
["bun", "run", fileURLToPath(new URL("../src/server.ts", import.meta.url))],
|
||||
{ stdin: "pipe", stdout: "pipe", stderr: "pipe" },
|
||||
);
|
||||
const uri = "file:///reopen.wrn";
|
||||
const withoutView = `page A {
|
||||
functions {
|
||||
x
|
||||
}
|
||||
}
|
||||
`;
|
||||
const withView = `page A {
|
||||
view {
|
||||
<
|
||||
}
|
||||
}
|
||||
`;
|
||||
const position = { line: 2, character: 5 };
|
||||
const reader = process.stdout.getReader();
|
||||
let output = "";
|
||||
async function readUntil(marker: string): Promise<void> {
|
||||
while (!output.includes(marker)) {
|
||||
const chunk = await reader.read();
|
||||
if (chunk.done) break;
|
||||
output += new TextDecoder().decode(chunk.value);
|
||||
}
|
||||
}
|
||||
|
||||
process.stdin.write(packet({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }));
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":1');
|
||||
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
method: "textDocument/didOpen",
|
||||
params: { textDocument: { uri, version: 1, text: withoutView } },
|
||||
}),
|
||||
);
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
id: 2,
|
||||
method: "textDocument/completion",
|
||||
params: { textDocument: { uri }, position },
|
||||
}),
|
||||
);
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":2');
|
||||
const firstReply = output.slice(output.indexOf('"id":2'));
|
||||
expect(firstReply).not.toContain('"label":"div"');
|
||||
|
||||
process.stdin.write(
|
||||
packet({ jsonrpc: "2.0", method: "textDocument/didClose", params: { textDocument: { uri } } }),
|
||||
);
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
method: "textDocument/didOpen",
|
||||
params: { textDocument: { uri, version: 1, text: withView } },
|
||||
}),
|
||||
);
|
||||
process.stdin.write(
|
||||
packet({
|
||||
jsonrpc: "2.0",
|
||||
id: 3,
|
||||
method: "textDocument/completion",
|
||||
params: { textDocument: { uri }, position },
|
||||
}),
|
||||
);
|
||||
await process.stdin.flush();
|
||||
await readUntil('"id":3');
|
||||
const secondReply = output.slice(output.indexOf('"id":3'));
|
||||
expect(secondReply).toContain('"label":"div"');
|
||||
|
||||
process.kill();
|
||||
await process.exited;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user