Files

100 lines
3.9 KiB
TypeScript
Raw Permalink Normal View History

/**
* 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();
});
});