100 lines
3.9 KiB
TypeScript
100 lines
3.9 KiB
TypeScript
|
|
/**
|
|||
|
|
* Task G3 – Titelblock: Blockdefinition mit attributierten Texten
|
|||
|
|
* (Projekt/Datum/Maßstab/Autor) + auto-insert als Layout-Frame.
|
|||
|
|
*
|
|||
|
|
* Die Text-Elemente tragen tag/attdef-Metadaten (F5/F6-Konvention),
|
|||
|
|
* sodass der Titelblock als DXF ATTDEF/ATTRIB roundtripped und das
|
|||
|
|
* spätere PropertiesPanel (D12) das Formular daraus bauen kann.
|
|||
|
|
*/
|
|||
|
|
import { describe, it, expect } from 'vitest';
|
|||
|
|
import { CADDocument } from '../src/kernel/document/CADDocument';
|
|||
|
|
import { createInMemoryDoc } from './helpers/inMemoryDoc';
|
|||
|
|
import {
|
|||
|
|
createTitleBlockElements,
|
|||
|
|
insertTitleBlockForLayout,
|
|||
|
|
TITLE_BLOCK_NAME,
|
|||
|
|
type TitleBlockMeta,
|
|||
|
|
} from '../src/services/titleBlockService';
|
|||
|
|
import type { LayoutDefinition } from '../src/types/cad.types';
|
|||
|
|
|
|||
|
|
const META: TitleBlockMeta = {
|
|||
|
|
project: 'Musterhalle A4',
|
|||
|
|
date: '2026-08-29',
|
|||
|
|
scale: '1:100',
|
|||
|
|
author: 'L. Admin',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
function mkDoc(): CADDocument {
|
|||
|
|
const { ydoc } = createInMemoryDoc();
|
|||
|
|
return new CADDocument(ydoc);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const LAYOUT: LayoutDefinition = {
|
|||
|
|
id: 'layout-1',
|
|||
|
|
name: 'A4 Plan',
|
|||
|
|
viewport: { center: { x: 0, y: 0 }, scale: 1 },
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
describe('G3: createTitleBlockElements', () => {
|
|||
|
|
it('Rahmen + Titel + 4 attributierte Texte mit Standard-Tags', () => {
|
|||
|
|
const els = createTitleBlockElements(META);
|
|||
|
|
const rects = els.filter((e) => e.type === 'rect');
|
|||
|
|
const texts = els.filter((e) => e.type === 'text');
|
|||
|
|
expect(rects.length).toBeGreaterThanOrEqual(1);
|
|||
|
|
expect(texts.length).toBe(5); // Titel + PROJEKT + DATUM + MASSSTAB + AUTOR
|
|||
|
|
const tags = texts.map((t) => t.properties.tag).filter(Boolean);
|
|||
|
|
expect(tags).toEqual(['PROJEKT', 'DATUM', 'MASSSTAB', 'AUTOR']);
|
|||
|
|
// Nur die attributierten Texte tragen attdef (Titel-Label bewusst nicht):
|
|||
|
|
const attrTexts = texts.filter((t) => t.properties.tag);
|
|||
|
|
expect(attrTexts.every((t) => t.properties.attdef === true)).toBe(true);
|
|||
|
|
// Werte stehen im Text
|
|||
|
|
const proj = texts.find((t) => t.properties.tag === 'PROJEKT');
|
|||
|
|
expect(String(proj!.properties.text)).toContain('Musterhalle A4');
|
|||
|
|
const autor = texts.find((t) => t.properties.tag === 'AUTOR');
|
|||
|
|
expect(String(autor!.properties.text)).toContain('L. Admin');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('Texte folgen der tag=Wert-Konvention des F5-ATTDEF-Parsers', () => {
|
|||
|
|
const els = createTitleBlockElements(META);
|
|||
|
|
const scale = els.find((e) => e.properties.tag === 'MASSSTAB')!;
|
|||
|
|
expect(String(scale.properties.text)).toBe('MASSSTAB=1:100');
|
|||
|
|
expect(scale.properties.prompt).toBeTruthy();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('leere Meta erzeugt Füllzeichen statt undefined', () => {
|
|||
|
|
const els = createTitleBlockElements({});
|
|||
|
|
const proj = els.find((e) => e.properties.tag === 'PROJEKT')!;
|
|||
|
|
expect(String(proj.properties.text)).toBe('PROJEKT=—');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe('G3: insertTitleBlockForLayout (auto-insert als Frame)', () => {
|
|||
|
|
it('erzeugt Blockdefinition im Doc und setzt frameBlockRef im Layout', () => {
|
|||
|
|
const doc = mkDoc();
|
|||
|
|
doc.addLayout(LAYOUT);
|
|||
|
|
const blockId = insertTitleBlockForLayout(doc, 'layout-1', META);
|
|||
|
|
expect(typeof blockId).toBe('string');
|
|||
|
|
const layout = doc.getLayout('layout-1');
|
|||
|
|
expect(layout!.frameBlockRef).toBe(blockId);
|
|||
|
|
const block = doc.getBlock(blockId);
|
|||
|
|
expect(block).toBeDefined();
|
|||
|
|
expect(block!.name).toBe(TITLE_BLOCK_NAME);
|
|||
|
|
expect(block!.category).toBe('frame');
|
|||
|
|
expect(block!.elements.length).toBe(6); // 1 rect + 5 text
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('vorhandener frameBlockRef wird ersetzt (kein Duplikat-Block je Re-Insert)', () => {
|
|||
|
|
const doc = mkDoc();
|
|||
|
|
doc.addLayout(LAYOUT);
|
|||
|
|
const first = insertTitleBlockForLayout(doc, 'layout-1', META);
|
|||
|
|
const second = insertTitleBlockForLayout(doc, 'layout-1', { ...META, project: 'Neu' });
|
|||
|
|
expect(second).not.toBe(first);
|
|||
|
|
expect(doc.getLayout('layout-1')!.frameBlockRef).toBe(second);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('Layout ohne frameBlockRef bleibt unverändert bei fehlendem Layout (throws)', () => {
|
|||
|
|
const doc = mkDoc();
|
|||
|
|
expect(() => insertTitleBlockForLayout(doc, 'gibts-nicht', META)).toThrow();
|
|||
|
|
});
|
|||
|
|
});
|