Files
Aislo/resources/tester/cad/table.test.ts
T
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt`
가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는
시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음.

- `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침**
- `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시)
- 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q`
  옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0

⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:12:30 +09:00

124 lines
4.4 KiB
TypeScript

/**
* 표 객체 검증 (조사표 5절 표 · 12절 테이블 셀).
* 실행: cd B07_DesignDetail/openwebcad && npx vitest run --config vitest.tmp.config.ts
*/
import { Point } from '@flatten-js/core';
import { describe, expect, it } from 'vitest';
import {
cellAt,
cellRect,
normalizeCells,
tableBorders,
type TableCells,
} from '../../../B07_DesignDetail/openwebcad/src/helpers/table-geometry';
import { TableEntity } from '../../../B07_DesignDetail/openwebcad/src/entities/TableEntity';
const origin = { x: 0, y: 100 };
const widths = [20, 30, 10];
const heights = [10, 10];
describe('표 기하', () => {
it('병합이 없으면 바깥 4 + 안쪽 세로 2×2 + 안쪽 가로 3 = 11개 경계선', () => {
const cells = normalizeCells([], 2, 3);
expect(tableBorders(origin, widths, heights, cells)).toHaveLength(11);
});
it('가로 병합은 그 자리의 세로선을 지운다', () => {
const cells: TableCells = normalizeCells(
[[{ text: '머리', colSpan: 3 }, null, null], [{ text: 'a' }, { text: 'b' }, { text: 'c' }]],
2,
3
);
const borders = tableBorders(origin, widths, heights, cells);
// 첫 행의 세로선 2개가 사라진다
expect(borders).toHaveLength(9);
const topRowVerticals = borders.filter(
(border) => border.x1 === border.x2 && border.y1 === 100 && border.x1 !== 0 && border.x1 !== 60
);
expect(topRowVerticals).toHaveLength(0);
});
it('세로 병합은 그 자리의 가로선을 지우고 셀 사각형이 두 행을 덮는다', () => {
const cells = normalizeCells(
[
[{ text: '그룹', rowSpan: 2 }, { text: 'b' }, { text: 'c' }],
[null, { text: 'e' }, { text: 'f' }],
],
2,
3
);
const borders = tableBorders(origin, widths, heights, cells);
const middleHorizontals = borders.filter((border) => border.y1 === 90 && border.y2 === 90);
expect(middleHorizontals).toHaveLength(2); // 첫 열은 병합이라 빠진다
const rect = cellRect(origin, widths, heights, 0, 0, cells[0][0]);
expect([rect.left, rect.top, rect.right, rect.bottom]).toEqual([0, 100, 20, 80]);
});
it('클릭 지점의 칸을 찾고, 병합 자리는 앵커를 돌려준다', () => {
const cells = normalizeCells(
[
[{ text: '그룹', rowSpan: 2 }, { text: 'b' }, { text: 'c' }],
[null, { text: 'e' }, { text: 'f' }],
],
2,
3
);
expect(cellAt(origin, widths, heights, cells, { x: 30, y: 95 })).toEqual({ row: 0, column: 1 });
// 아래쪽 병합 자리를 찍어도 앵커(0,0)가 나온다
expect(cellAt(origin, widths, heights, cells, { x: 10, y: 85 })).toEqual({ row: 0, column: 0 });
expect(cellAt(origin, widths, heights, cells, { x: 200, y: 95 })).toBeNull();
});
it('빈 칸을 채워도 병합은 풀리지 않는다', () => {
const normalized = normalizeCells([[{ text: '머리', colSpan: 3 }]], 2, 3);
expect(normalized[0][1]).toBeNull();
expect(normalized[0][2]).toBeNull();
expect(normalized[1][0]).toEqual({ text: '' });
});
});
describe('표 객체', () => {
const makeTable = () =>
new TableEntity('layer-1', new Point(0, 100), [...widths], [...heights], [
[{ text: '머리', colSpan: 3 }, null, null],
[{ text: 'a' }, { text: 'b', key: 'cut_soil' }, { text: 'c' }],
]);
it('경계상자는 열 폭·행 높이의 합이다', () => {
const box = makeTable().getBoundingBox();
expect([box.xmin, box.ymin, box.xmax, box.ymax]).toEqual([0, 80, 60, 100]);
});
it('JSON 왕복에서 병합·칸 키가 살아남는다', async () => {
const json = await makeTable().toJson();
expect(json).toBeTruthy();
if (!json) return;
const restored = await TableEntity.fromJson(json as never);
expect(restored.getCell(0, 0)?.colSpan).toBe(3);
expect(restored.getCell(0, 1)).toBeNull();
expect(restored.getCell(1, 1)?.key).toBe('cut_soil');
const box = restored.getBoundingBox();
expect([box.xmin, box.ymin, box.xmax, box.ymax]).toEqual([0, 80, 60, 100]);
});
it('행·열을 넣고 지울 수 있다', () => {
const table = makeTable();
table.insertRow(2);
expect(table.getRowHeights()).toHaveLength(3);
table.insertColumn(0);
expect(table.getColumnWidths()).toHaveLength(4);
table.deleteColumn(0);
table.deleteRow(2);
expect(table.getColumnWidths()).toEqual(widths);
expect(table.getRowHeights()).toEqual(heights);
});
it('열 폭 그립으로 폭만 바뀌고 전체 폭이 따라 늘어난다', () => {
const table = makeTable();
table.setColumnWidth(0, 40);
const box = table.getBoundingBox();
expect(box.xmax - box.xmin).toBe(80);
});
});