task(G3): title block with attributes - frame + attributed texts (ATTDEF convention), auto-insert as layout frame block
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user