New oracle DocuRun on Java's JDocuElement. Italic is kept (the model knew only bold), an item keeps its file's size and font size (Swift's 32 x 24 and 8-72 changed Java's files), an item without a colour is Java's light grey. Java's new items, colour lists, font sizes and styles, the drag's smallest sizes and Java's stacking; document commands for the editor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
157 lines
9.1 KiB
TypeScript
157 lines
9.1 KiB
TypeScript
// The drawing of a document against the project it holds, for every file of
|
|
// the corpus: the same devices in the same order at the same places, Java's
|
|
// labels, the cables as the export draws them (wireless or not), the
|
|
// documentation items in the model's render order.
|
|
|
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { describe, expect, it } from 'vitest';
|
|
import * as Y from 'yjs';
|
|
import { inJavaDrawingOrder, stableGeneratedMacAddress } from '@mdg-filius/core';
|
|
import { exportProject, openProjectArchive, type ImportedProject } from '@mdg-filius/fls';
|
|
import { addDevice, captureCards, desktopView, readProject, renameDevice, setHostAddress, setHostLabels, setInterfaceAddress, topologyView, writeProject } from '../src/index.js';
|
|
|
|
const corpus = join(dirname(fileURLToPath(import.meta.url)), '../../../corpus');
|
|
const files = ['java-examples', 'java-tests', 'ipad-fixtures'].flatMap((folder) =>
|
|
readdirSync(join(corpus, folder))
|
|
.filter((name) => name.endsWith('.fls'))
|
|
.map((name) => join(folder, name)),
|
|
);
|
|
|
|
function docOf(file: string, change: (project: ImportedProject) => void = () => {}): Y.Doc {
|
|
let counter = 0;
|
|
const project = openProjectArchive(new Uint8Array(readFileSync(join(corpus, file))), () => `00000000-0000-0000-0000-${(0xffff_ffff - ++counter).toString(16).padStart(12, '0')}`);
|
|
change(project);
|
|
const doc = new Y.Doc();
|
|
writeProject(doc, project);
|
|
return doc;
|
|
}
|
|
|
|
describe('the drawing of a document', () => {
|
|
for (const file of files) {
|
|
it(file, () => {
|
|
const doc = docOf(file);
|
|
const project = readProject(doc);
|
|
const view = topologyView(doc);
|
|
expect(view.nodes.map((node) => [node.id, node.kind, node.name, node.x, node.y])).toEqual(
|
|
project.graph.nodes.map((node) => [node.id, node.kind, node.displayName, node.position.x, node.position.y]),
|
|
);
|
|
expect(view.nodes.map((node) => node.ports)).toEqual(project.graph.nodes.map((node) => node.ports.map((port) => ({ id: port.id, label: port.label }))));
|
|
expect(view.links.map((link) => [link.id, link.sourceNode, link.sourcePort, link.targetNode, link.targetPort])).toEqual(
|
|
project.graph.links.map((link) => [link.id, link.sourceNodeId, link.sourcePortId, link.targetNodeId, link.targetPortId]),
|
|
);
|
|
expect(view.links.map((link) => link.wireless)).toEqual(exportProject(project).topology.links.map((link) => link.wireless));
|
|
// In Java's drawing order, the last on top.
|
|
expect(
|
|
view.docs.map(({ id, kind, x, y, width, height, text, color, font, size, bold, italic, order }) => ({
|
|
id,
|
|
kind,
|
|
frame: { x, y, width, height },
|
|
text,
|
|
color,
|
|
fontName: font,
|
|
fontSize: size,
|
|
isBold: bold,
|
|
isItalic: italic,
|
|
order,
|
|
})),
|
|
).toEqual(inJavaDrawingOrder(project.documentationItems));
|
|
});
|
|
}
|
|
|
|
it('draws the Internet of the NAT example as a cloud', () => {
|
|
const view = topologyView(docOf('java-examples/nat.fls'));
|
|
expect(view.nodes.filter((node) => node.cloud).map((node) => node.name)).toEqual(['Internet']);
|
|
});
|
|
|
|
it("writes a host's label as Java does: its name, address, MAC address or both", () => {
|
|
const labels = (policy: 'manual' | 'ipAddress' | 'macAddress' | 'ipAndMAC') => {
|
|
const doc = docOf('java-examples/zwei_rechnernetze_komplett.fls', (project) => {
|
|
for (const node of project.graph.nodes) node.hostLabelPolicy = policy;
|
|
});
|
|
const project = readProject(doc);
|
|
const notebook = project.graph.nodes.find((node) => node.displayName === 'Notebook 1.10')!;
|
|
const view = topologyView(doc);
|
|
return { label: view.nodes.find((node) => node.id === notebook.id)!.label, mac: notebook.ports[0]!.importedMacAddress ?? stableGeneratedMacAddress(notebook.ports[0]!.id), switchLabel: view.nodes.find((node) => node.kind === 'networkSwitch')!.label };
|
|
};
|
|
expect(labels('manual').label).toBe('Notebook 1.10');
|
|
expect(labels('ipAddress').label).toBe('141.99.1.10');
|
|
const { label, mac, switchLabel } = labels('ipAndMAC');
|
|
expect(label).toBe(`141.99.1.10 (${mac})`);
|
|
expect(labels('macAddress').label).toBe(mac);
|
|
// Only hosts show an address (Host.holeAnzeigeName).
|
|
expect(switchLabel).toBe('Switch 1');
|
|
});
|
|
});
|
|
|
|
describe("a computer's desktop", () => {
|
|
it('names its programs and its label, and whether the label is its address, for computers only', () => {
|
|
for (const file of files) {
|
|
const doc = docOf(file);
|
|
const project = readProject(doc);
|
|
for (const node of project.graph.nodes) {
|
|
const desktop = desktopView(doc, node.id);
|
|
if (node.kind !== 'pc' && node.kind !== 'notebook') {
|
|
expect(desktop, `${file} ${node.displayName}`).toBeUndefined();
|
|
continue;
|
|
}
|
|
expect(new Set(desktop!.programs), `${file} ${node.displayName}`).toEqual(project.installedPrograms.get(node.id) ?? new Set());
|
|
expect(desktop!.kind).toBe(node.kind);
|
|
expect(desktop!.label).toBe(topologyView(doc).nodes.find((each) => each.id === node.id)!.label);
|
|
expect(desktop!.addressAsName).toBe(node.hostLabelPolicy === 'ipAddress' || node.hostLabelPolicy === 'ipAndMAC');
|
|
}
|
|
}
|
|
expect(desktopView(docOf(files[0]!), 'nobody')).toBeUndefined();
|
|
});
|
|
|
|
it("takes a label of address and MAC address for the address, as Java's title does (isUseIPAsName), the MAC address alone not", () => {
|
|
const doc = new Y.Doc();
|
|
let counter = 0;
|
|
addDevice(doc, { kind: 'pc', id: 'pc', name: 'PC', x: 0, y: 0, newId: () => `port-${++counter}` }, 'me');
|
|
expect(desktopView(doc, 'pc')).toMatchObject({ kind: 'pc', label: 'PC', addressAsName: false, programs: [] });
|
|
setHostLabels(doc, 'pc', { ipAddress: true, macAddress: true }, 'me');
|
|
expect(desktopView(doc, 'pc')!.addressAsName).toBe(true);
|
|
setHostLabels(doc, 'pc', { ipAddress: false, macAddress: true }, 'me');
|
|
expect(desktopView(doc, 'pc')!.addressAsName).toBe(false);
|
|
setHostLabels(doc, 'pc', { ipAddress: true, macAddress: false }, 'me');
|
|
expect(desktopView(doc, 'pc')).toMatchObject({ label: '192.168.0.10', addressAsName: true });
|
|
});
|
|
|
|
it("offers each card's data exchange with the title of Java's tab: the address where the label is it, else the label and the address", () => {
|
|
// Java's titles (packages/core/test/exchangeWindow.java.json): "Anna - 192.168.0.20", a computer named so; "192.168.0.30", one labelled by its address.
|
|
const java = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../core/test/exchangeWindow.java.json'), 'utf8')) as { steps: { step: string; titles: string[] }[] };
|
|
const doc = new Y.Doc();
|
|
let counter = 0;
|
|
const newId = () => `port-${++counter}`;
|
|
addDevice(doc, { kind: 'pc', id: 'anna', name: 'Anna', x: 0, y: 0, newId }, 'me');
|
|
setHostAddress(doc, 'anna', 'ipAddress', '192.168.0.20', 'me');
|
|
addDevice(doc, { kind: 'notebook', id: 'server', name: 'Server', x: 0, y: 0, newId }, 'me');
|
|
setHostAddress(doc, 'server', 'ipAddress', '192.168.0.30', 'me');
|
|
setHostLabels(doc, 'server', { ipAddress: true, macAddress: false }, 'me');
|
|
expect([...captureCards(doc, 'anna'), ...captureCards(doc, 'server')].map((card) => card.title)).toEqual(java.steps.find((step) => step.step === 'the server\'s card beside it')!.titles);
|
|
expect(captureCards(doc, 'anna')).toEqual([{ portId: 'port-1', ipAddress: '192.168.0.20', title: 'Anna - 192.168.0.20' }]);
|
|
// Address and MAC address: the address; the MAC address alone: the label, which is it.
|
|
setHostLabels(doc, 'server', { ipAddress: true, macAddress: true }, 'me');
|
|
expect(captureCards(doc, 'server')[0]!.title).toBe('192.168.0.30');
|
|
setHostLabels(doc, 'server', { ipAddress: false, macAddress: true }, 'me');
|
|
expect(captureCards(doc, 'server')[0]!.title).toBe(`${stableGeneratedMacAddress('port-2')} - 192.168.0.30`);
|
|
|
|
// A router's cards in the order of its ports, each with its own address; a home router's WAN card first.
|
|
addDevice(doc, { kind: 'router', id: 'router', name: 'Vermittlungsrechner', x: 0, y: 0, interfaces: 2, newId }, 'me');
|
|
const [first, second] = topologyView(doc).nodes.find((node) => node.id === 'router')!.ports;
|
|
setInterfaceAddress(doc, 'router', second!.id, 'ipAddress', '10.0.0.1', 'me');
|
|
renameDevice(doc, 'router', 'R1', 'me');
|
|
expect(captureCards(doc, 'router').map((card) => [card.portId, card.title])).toEqual([
|
|
[first!.id, `R1 - ${captureCards(doc, 'router')[0]!.ipAddress}`],
|
|
[second!.id, 'R1 - 10.0.0.1'],
|
|
]);
|
|
addDevice(doc, { kind: 'gateway', id: 'gateway', name: 'Heimrouter', x: 0, y: 0, newId }, 'me');
|
|
expect(captureCards(doc, 'gateway').map((card) => card.portId)).toEqual(topologyView(doc).nodes.find((node) => node.id === 'gateway')!.ports.map((port) => port.id));
|
|
// No cards: a switch, a modem; no device.
|
|
addDevice(doc, { kind: 'networkSwitch', id: 'switch', name: 'Switch', x: 0, y: 0, newId }, 'me');
|
|
addDevice(doc, { kind: 'remoteLink', id: 'modem', name: 'Modem', x: 0, y: 0, newId }, 'me');
|
|
expect([captureCards(doc, 'switch'), captureCards(doc, 'modem'), captureCards(doc, 'nobody')]).toEqual([[], [], []]);
|
|
});
|
|
});
|