Files
Pier_v1/tests/abut-edit.test.ts
ypshimandClaude Opus 5.5 886347471e feat(plan): 날개벽 형식을 좌 · 우 1행으로 요약하고 "반대쪽에도 적용" 추가
- 선택 줄을 "날개벽 형식  좌 [일반형 · 단축형 · 없음] │ 우 [일반형 · 단축형 · 없음]" 한 줄로 합침 (작은 그림 타일). "좌우" · "편집할 측" 줄과 ① 쪽 / ⑤ 쪽 표시는 없앰 (위치는 모델창에서 확인)
- 타일이나 "좌" / "우" 글자를 누르면 그 측이 편집 대상이 되고, 편집 중인 묶음은 진한 테두리로 표시. 아래 치수 입력과 측면 삽도가 그 측을 따름
- "좌우 같게" 삭제: 형식과 치수는 측별로만 저장. 예전 파일의 wingsLinked 값은 읽기만 하고 쓰지 않음
- 반대쪽에도 적용 (copyWingDims): 두께 · 끝 높이 · 헌치를 한 번 복사하고, 길이는 두 측 형식이 같을 때만 복사. 반대쪽이 없음이면 비활성
- 테스트 갱신. 전체 670건 통과, 타입 검사 · 빌드 통과. 브라우저 시각 확인은 미실시

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 09:36:53 +09:00

77 lines
4.1 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
import { describe, expect, it } from 'vitest';
import { applyAbutEdit, applyBearingEdit, restoreAbutDefault, setWingForm, applyWingDim, copyWingDims } from '../src/core/abut/edit';
import { parseAbutments } from '../src/core/abut/parse';
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0];
describe('교대 입력 편집 (교대 편집 카드)', () => {
it('a valid change returns a new record and leaves the old one unchanged', () => {
const next = applyAbutEdit(rec, 'groundEl', 34.5);
expect(next.groundEl).toBe(34.5);
expect(rec.groundEl).toBeCloseTo(33.984, 3);
});
it('a non-number, a pile length of 0 or a skew out of range are refused with the field name', () => {
expect(() => applyAbutEdit(rec, 'seatEl', NaN)).toThrow('교좌면 EL');
expect(() => applyAbutEdit(rec, 'pileLengthMm', 0)).toThrow('말뚝길이');
expect(() => applyAbutEdit(rec, 'skewDeg', 200)).toThrow('skew');
});
it('the 교좌면 EL must stay above the 기초 하단 EL', () => {
expect(() => applyAbutEdit(rec, 'seatEl', rec.foundationBottomEl)).toThrow('기초 하단 EL보다 높아야');
});
it('a wing form change sets one side and keeps the other (좌 · 우 따로)', () => {
expect(setWingForm(rec, 'left', 'short').wings).toEqual({ left: 'short', right: 'general' });
expect(rec.wings).toBeUndefined();
});
it('wing dims are per side; 0 thickness and a negative haunch are refused', () => {
expect(applyWingDim(rec, 'left', 'lengthMm', 6000).wingDims).toEqual({ left: { lengthMm: 6000 } });
expect(() => applyWingDim(rec, 'left', 'thicknessMm', 0)).toThrow('두께');
expect(() => applyWingDim(rec, 'left', 'haunchMm', -1)).toThrow('헌치');
});
it('반대쪽에도 적용: copies 두께 · 끝 높이 · 헌치 once, and the 길이 only when both forms match', () => {
let r = applyWingDim(applyWingDim(rec, 'right', 'lengthMm', 6000), 'right', 'thicknessMm', 500);
expect(copyWingDims(r, 'right').wingDims?.left).toEqual({ lengthMm: 6000, thicknessMm: 500 });
r = setWingForm(r, 'left', 'short');
expect(copyWingDims(r, 'right').wingDims?.left).toEqual({ thicknessMm: 500 }); // 형식이 달라 길이는 복사하지 않음
expect(() => copyWingDims(setWingForm(r, 'left', 'none'), 'right')).toThrow('반대쪽');
// one copy, no link: a later change on the right does not move the left
const copied = copyWingDims(applyWingDim(rec, 'right', 'haunchMm', 200), 'right');
expect(applyWingDim(copied, 'right', 'haunchMm', 250).wingDims?.left).toEqual({ haunchMm: 200 });
});
});
describe('교대 기본값 복원과 받침 편집', () => {
it('restores the imported defaults after several changes (a copy, not the same object)', () => {
const changed = setWingForm(applyAbutEdit(rec, 'groundEl', 34.5), 'left', 'none');
const back = restoreAbutDefault(rec);
expect(back).toEqual(rec);
expect(back).not.toBe(rec);
expect(changed.groundEl).toBe(34.5);
});
it('a bearing z change keeps the others; a 교좌면 override sets that bearing only', () => {
const z = applyBearingEdit(rec, 3, 'z', 44.7);
expect(z.bearings.find((b) => b.no === 3)!.z).toBe(44.7);
expect(z.bearings.find((b) => b.no === 1)!.z).toBe(rec.bearings[0].z);
const seat = applyBearingEdit(rec, 2, 'seatEl', 44.5);
expect(seat.bearings.find((b) => b.no === 2)!.seatEl).toBe(44.5);
const auto = applyBearingEdit(seat, 2, 'seatEl', undefined);
expect(auto.bearings.find((b) => b.no === 2)!.seatEl).toBeUndefined();
});
it('refuses an unknown bearing, a non-number z, and a 교좌면 at or below the 기초 하단', () => {
expect(() => applyBearingEdit(rec, 99, 'z', 44)).toThrow('받침 99번');
expect(() => applyBearingEdit(rec, 1, 'z', undefined)).toThrow('z는 숫자');
expect(() => applyBearingEdit(rec, 1, 'seatEl', rec.foundationBottomEl)).toThrow('기초 하단');
});
});