Add New Diagram command with starter templates
- Archify: New Diagram… (Explorer folder menu, File > New File…, Command Palette) creates a .archify file from a template and opens the diagram - Six YAML templates: architecture, workflow, sequence, dataflow, lifecycle and an everyday plan; connections carry ids, workflows use schema 2 - Title and meta.output follow the file name; template comments are kept - Tests: every template passes archify validate; integration check for the command Closes #2
This commit is contained in:
@@ -97,6 +97,35 @@ export async function run(): Promise<void> {
|
||||
assert.ok(diagnostics[0].range.start.line >= 2, `reported on line ${diagnostics[0].range.start.line + 1}`);
|
||||
});
|
||||
|
||||
await check('New Diagram creates a .archify file from a template and opens the diagram', async () => {
|
||||
const folder = workspaceFile('.');
|
||||
// Stub the template pick and the name prompt; check the name validation on the way.
|
||||
const window = vscode.window as unknown as Record<string, unknown>;
|
||||
const originalPick = window.showQuickPick;
|
||||
const originalInput = window.showInputBox;
|
||||
let existingNameMessage: unknown;
|
||||
window.showQuickPick = async (items: readonly { label: string }[]) => (await items).find((item) => item.label === 'Everyday Plan');
|
||||
window.showInputBox = async (options: vscode.InputBoxOptions) => {
|
||||
existingNameMessage = await options.validateInput?.('web-platform');
|
||||
return 'summer-plan';
|
||||
};
|
||||
try {
|
||||
await vscode.commands.executeCommand('archify.newDiagram', folder);
|
||||
} finally {
|
||||
window.showQuickPick = originalPick;
|
||||
window.showInputBox = originalInput;
|
||||
}
|
||||
assert.match(String(existingNameMessage), /already exists/);
|
||||
const uri = vscode.Uri.joinPath(folder, 'summer-plan.archify');
|
||||
const text = new TextDecoder().decode(await vscode.workspace.fs.readFile(uri));
|
||||
assert.match(text, /^diagram_type: workflow$/m);
|
||||
assert.match(text, /^ {2}title: Summer plan$/m);
|
||||
assert.match(text, /^ {2}output: summer-plan\.html$/m);
|
||||
await waitFor('diagram editor for the new file', () =>
|
||||
webviewTabs('archify.diagram').some((tab) => tab.input instanceof vscode.TabInputCustom && tab.input.uri.toString() === uri.toString()),
|
||||
);
|
||||
});
|
||||
|
||||
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', () => {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { test } from 'node:test';
|
||||
import { parse } from 'yaml';
|
||||
import { readDiagramSource } from '../src/detect';
|
||||
import { validate } from '../src/renderer';
|
||||
import { diagramFromTemplate, diagramName, invalidNameReason, TEMPLATES, titleFromName } from '../src/templates';
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const templates = path.join(root, 'templates');
|
||||
const runtime = {
|
||||
cliPath: path.join(root, 'vendor', 'archify', 'bin', 'archify.mjs'),
|
||||
nodePath: process.execPath,
|
||||
runAsNode: false,
|
||||
workDir: fs.mkdtempSync(path.join(os.tmpdir(), 'archify-ext-test-')),
|
||||
};
|
||||
|
||||
test('every template file is listed and every listed template exists', () => {
|
||||
const files = fs.readdirSync(templates).filter((file) => file.endsWith('.archify')).sort();
|
||||
assert.deepEqual(TEMPLATES.map((item) => item.file).sort(), files);
|
||||
});
|
||||
|
||||
for (const item of TEMPLATES) {
|
||||
test(`template ${item.file} is a valid ${item.type} diagram`, async () => {
|
||||
const text = diagramFromTemplate(fs.readFileSync(path.join(templates, item.file), 'utf8'), item.defaultName);
|
||||
const source = readDiagramSource(text, `${item.defaultName}.archify`);
|
||||
assert.ok(source);
|
||||
assert.equal(source.type, item.type);
|
||||
assert.equal(source.format, 'yaml');
|
||||
const result = await validate(runtime, source.type, source.json);
|
||||
assert.deepEqual(
|
||||
result.diagnostics.filter((d) => d.severity === 'error').map((d) => `${d.code}: ${d.message}`),
|
||||
[],
|
||||
);
|
||||
assert.ok(result.ok);
|
||||
});
|
||||
}
|
||||
|
||||
test('diagramFromTemplate sets title and output and keeps comments and flow lists', () => {
|
||||
const text = diagramFromTemplate('# keep me\nschema_version: 1\nmeta:\n title: Old\n output: old.html\npos: [1, 2]\n', 'sales-report');
|
||||
assert.match(text, /^# keep me/);
|
||||
assert.match(text, /pos: \[1, 2\]/);
|
||||
assert.deepEqual(parse(text).meta, { title: 'Sales report', output: 'sales-report.html' });
|
||||
});
|
||||
|
||||
test('diagramFromTemplate adds meta when the template has none', () => {
|
||||
assert.deepEqual(parse(diagramFromTemplate('diagram_type: workflow\n', 'plan')).meta, { title: 'Plan', output: 'plan.html' });
|
||||
});
|
||||
|
||||
test('diagram names', () => {
|
||||
assert.equal(diagramName(' web.archify '), 'web');
|
||||
assert.equal(diagramName('web.ARCHIFY'), 'web');
|
||||
assert.equal(invalidNameReason('web'), undefined);
|
||||
assert.equal(invalidNameReason('my plan 2026'), undefined);
|
||||
assert.ok(invalidNameReason(''));
|
||||
assert.ok(invalidNameReason('.archify'));
|
||||
assert.ok(invalidNameReason('..'));
|
||||
assert.ok(invalidNameReason('a/b'));
|
||||
assert.ok(invalidNameReason('a:b'));
|
||||
assert.equal(titleFromName('sales-report_v2'), 'Sales report v2');
|
||||
assert.equal(titleFromName('plan'), 'Plan');
|
||||
});
|
||||
Reference in New Issue
Block a user