Add Archify | New Model… with three C4 examples and Open Model in the Explorer menu

- templates/models: C4 - Standard Web App, C4 - E-Shop, C4 - Event-Based
  Integration; each a complete model (main.archify, registry, context,
  containers, components) that validates
- archify.newModel: pick an example and a folder name, copy it, name the
  model after the folder, open the model page
- Explorer menu: New Model… on folders, Open Model on .archify and
  .archify-registry files; File → New File… lists New Model…
- unit test validates every example end to end; integration test runs the
  command; README, CHANGELOG, docs/model.md
This commit is contained in:
2026-10-19 12:12:49 +03:00
parent 7a2b661afe
commit 57914d435c
29 changed files with 874 additions and 8 deletions
+47
View File
@@ -340,4 +340,51 @@ export async function run(): Promise<void> {
assert.match(content, /id: new-shop/);
assert.match(content, /name: New shop/);
});
await check('Archify | New Model… creates a model folder from an example and opens its page', async () => {
const commands = vscode.extensions.getExtension(EXTENSION_ID)?.packageJSON.contributes.commands as { command: string; title: string }[];
assert.equal(commands.find((item) => item.command === 'archify.newModel')?.title, 'Archify | New Model…');
const parent = workspaceFile('.');
const window = vscode.window as unknown as Record<string, unknown>;
const originalPick = window.showQuickPick;
const originalInput = window.showInputBox;
let labels: string[] = [];
let existingNameMessage: unknown;
window.showQuickPick = async (items: readonly { label: string }[]) => {
labels = (await items).map((item) => item.label);
return (await items).find((item) => item.label === 'C4 - E-Shop');
};
window.showInputBox = async (options: vscode.InputBoxOptions) => {
existingNameMessage = await options.validateInput?.('new-shop');
return 'shop-two';
};
try {
await vscode.commands.executeCommand('archify.newModel', parent);
} finally {
window.showQuickPick = originalPick;
window.showInputBox = originalInput;
}
assert.deepEqual(labels, ['C4 - Standard Web App', 'C4 - E-Shop', 'C4 - Event-Based Integration']);
assert.match(String(existingNameMessage), /already exists/);
const folder = vscode.Uri.joinPath(parent, 'shop-two');
const main = vscode.Uri.joinPath(folder, 'main.archify');
const content = new TextDecoder().decode(await vscode.workspace.fs.readFile(main));
assert.match(content, /id: shop-two/);
assert.match(content, /name: Shop two/);
assert.match(content, /title: Shop two — overview/);
const names = (await vscode.workspace.fs.readDirectory(folder)).map(([name]) => name).sort();
assert.deepEqual(names, ['0100-context', '0200-containers', '0300-components', 'main.archify', 'registry']);
const registry = (await vscode.workspace.fs.readDirectory(vscode.Uri.joinPath(folder, 'registry'))).map(([name]) => name).sort();
assert.deepEqual(registry, ['containers.archify-registry', 'people.archify-registry', 'systems.archify-registry']);
const tab = await waitFor('model page tab', () =>
vscode.window.tabGroups.all.flatMap((g) => g.tabs).find((t) => t.isActive && t.input instanceof vscode.TabInputCustom && t.input.uri.toString() === main.toString()),
);
assert.ok(tab);
// The new model is indexed: its diagrams resolve against the copied registry.
const containers = await vscode.workspace.openTextDocument(vscode.Uri.joinPath(folder, '0200-containers', 'online-shop.archify'));
await vscode.window.showTextDocument(containers);
await new Promise((resolve) => setTimeout(resolve, 1500));
const errors = vscode.languages.getDiagnostics(containers.uri).filter((d) => d.severity === vscode.DiagnosticSeverity.Error);
assert.deepEqual(errors.map((d) => d.message), []);
});
}