Files
archify-vscode-ext/test/integration/suite.ts
T
root-at-skicandClaude Opus 5.5 30e2faa38c Support .archify diagram files (0.2.0)
.archify files are Archify JSON diagram sources. They open as JSON,
preview with the same commands, report validation problems, and get
completion from a schema that dispatches on diagram_type. Adds a
hello.archify example and tests for detection, the dispatching schema
and the VS Code behaviour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 12:56:12 +03:00

163 lines
8.1 KiB
TypeScript

import assert from 'node:assert/strict';
import path from 'node:path';
import * as vscode from 'vscode';
const EXTENSION_ID = 'vscode-extensions.archify-vscode-ext';
function workspaceFile(name: string): vscode.Uri {
const folder = vscode.workspace.workspaceFolders?.[0];
assert.ok(folder, 'a workspace folder is open');
return vscode.Uri.joinPath(folder.uri, name);
}
async function waitFor<T>(what: string, probe: () => T | undefined | false, timeoutMs = 20_000): Promise<T> {
const start = Date.now();
for (;;) {
const value = probe();
if (value) return value;
if (Date.now() - start > timeoutMs) throw new Error(`Timed out waiting for ${what}`);
await new Promise((resolve) => setTimeout(resolve, 200));
}
}
function webviewTabs(viewTypeSuffix: string): vscode.Tab[] {
return vscode.window.tabGroups.all
.flatMap((group) => group.tabs)
.filter((tab) =>
(tab.input instanceof vscode.TabInputWebview && tab.input.viewType.endsWith(viewTypeSuffix)) ||
(tab.input instanceof vscode.TabInputCustom && tab.input.viewType === viewTypeSuffix));
}
async function check(name: string, body: () => Promise<void>): Promise<void> {
try {
await body();
console.log(` ✔ ${name}`);
} catch (error) {
console.log(` ✖ ${name}\n ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
throw error;
}
}
export async function run(): Promise<void> {
console.log('Archify extension integration suite');
await check('activates on a diagram source and sets the context key', async () => {
const doc = await vscode.workspace.openTextDocument(workspaceFile('production-deployment.architecture.json'));
await vscode.window.showTextDocument(doc);
const extension = vscode.extensions.getExtension(EXTENSION_ID);
assert.ok(extension, 'extension is installed');
await waitFor('activation', () => extension.isActive);
});
await check('.archify files open as JSON with the preview available', async () => {
const doc = await vscode.workspace.openTextDocument(workspaceFile('web-platform.archify'));
assert.equal(doc.languageId, 'json');
await vscode.window.showTextDocument(doc);
await vscode.commands.executeCommand('archify.showPreviewToSide');
const tabs = await waitFor('.archify preview tab', () => {
const found = webviewTabs('archify.preview').filter((tab) => /web-platform\.archify/.test(tab.label));
return found.length > 0 && found;
});
assert.equal(tabs.length, 1);
});
await check('.archify completion comes from the schema of its diagram_type', async () => {
const doc = await vscode.workspace.openTextDocument(workspaceFile('web-platform.archify'));
await vscode.window.showTextDocument(doc);
// Complete the value of the first component's "type".
const offset = doc.getText().indexOf('"type": "') + '"type": "'.length;
const position = doc.positionAt(offset);
let labels: string[] = [];
for (let attempt = 0; attempt < 50 && !labels.includes('backend'); attempt += 1) {
const list = await vscode.commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', doc.uri, position);
labels = list.items.map((item) => (typeof item.label === 'string' ? item.label : item.label.label).replace(/"/g, ''));
if (!labels.includes('backend')) await new Promise((resolve) => setTimeout(resolve, 300));
}
assert.ok(labels.includes('backend') && labels.includes('database'), `offered: ${labels.join(', ')}`);
});
await check('.archify problems are reported against the file', async () => {
const good = await vscode.workspace.openTextDocument(workspaceFile('cache-miss.archify'));
const broken = good.getText().replace('"participants"', '"actors"');
const target = workspaceFile('broken.archify');
await vscode.workspace.fs.writeFile(target, new TextEncoder().encode(broken));
const doc = await vscode.workspace.openTextDocument(target);
await vscode.window.showTextDocument(doc);
const diagnostics = await waitFor('archify diagnostics on broken.archify', () => {
const found = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify');
return found.length > 0 && found;
});
assert.ok(diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error));
});
await check('opens a live preview beside the source', async () => {
await vscode.commands.executeCommand('archify.showPreviewToSide', workspaceFile('production-deployment.architecture.json'));
const tabs = await waitFor('preview tab', () => {
const found = webviewTabs('archify.preview').filter((tab) => /production-deployment/.test(tab.label));
return found.length > 0 && found;
});
assert.equal(tabs.length, 1);
assert.match(tabs[0].label, /Preview production-deployment\.architecture\.json/);
});
await check('reports validation problems in the Problems panel with JSON ranges', async () => {
const doc = await vscode.workspace.openTextDocument({
language: 'json',
content: JSON.stringify({ schema_version: 1, diagram_type: 'architecture', meta: { title: 'x' }, components: [{ id: 'a' }] }, null, 2),
});
await vscode.window.showTextDocument(doc);
const diagnostics = await waitFor('archify diagnostics', () => {
const found = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify');
return found.length > 0 && found;
});
const component = diagnostics.find((d) => /components\/0/.test(d.message));
assert.ok(component, 'a diagnostic for /components/0');
assert.equal(doc.getText(component.range), '{', 'range points at the component object');
assert.equal(component.severity, vscode.DiagnosticSeverity.Error);
});
await check('clears problems once the document is fixed', async () => {
const good = await vscode.workspace.openTextDocument(workspaceFile('event-stream.dataflow.json'));
const doc = await vscode.workspace.openTextDocument({ language: 'json', content: good.getText() });
await vscode.window.showTextDocument(doc);
await new Promise((resolve) => setTimeout(resolve, 3000));
const errors = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify' && d.severity === vscode.DiagnosticSeverity.Error);
assert.deepEqual(errors.map((d) => d.message), []);
});
await check('renders to an HTML file and opens it in the Archify viewer', async () => {
const source = workspaceFile('cache-miss-request.sequence.json');
const output = vscode.Uri.file(path.join(path.dirname(source.fsPath), 'cache-miss-request.sequence.html'));
// Stub the save dialog and the follow-up notification.
const window = vscode.window as unknown as Record<string, unknown>;
const originalSave = window.showSaveDialog;
const originalInfo = window.showInformationMessage;
window.showSaveDialog = async () => output;
window.showInformationMessage = async () => 'Open in Viewer';
try {
await vscode.commands.executeCommand('archify.renderToHtml', source);
} finally {
window.showSaveDialog = originalSave;
window.showInformationMessage = originalInfo;
}
const html = new TextDecoder().decode(await vscode.workspace.fs.readFile(output));
assert.match(html, /<meta name="generator" content="archify/);
await waitFor('custom editor tab', () => webviewTabs('archify.htmlViewer').length > 0);
});
await check('source-backed diagram previews with the evidence fallback', async () => {
const uri = workspaceFile('mco-runtime.architecture.json');
await vscode.commands.executeCommand('archify.showPreview', uri);
await waitFor('mco preview tab', () => webviewTabs('archify.preview').some((tab) => /mco-runtime/.test(tab.label)));
const doc = await vscode.workspace.openTextDocument(uri);
const diagnostics = await waitFor('evidence diagnostics', () => {
const found = vscode.languages.getDiagnostics(doc.uri).filter((d) => d.source === 'archify');
return found.length > 0 && found;
});
assert.ok(
diagnostics.every((d) => d.severity !== vscode.DiagnosticSeverity.Error),
'unverifiable evidence is a warning in fallback mode',
);
});
}