- LINE: 「첫 번째 점 지정」 빈 Enter = 마지막 끝점에서 이음 · U = 마지막 토막(되돌림 한 칸) · 둘째 토막부터 C · Enter · Esc = 끝(토막 남음) - PLINE(새 pline-tool): 호(A) 접선 호 · 길이(L) · 닫기(C · 호 모드 CL) · 명령 취소(U) · 선(L) · Esc · Enter = 그린 데까지 남김 - RECTANG: 치수(D) 길이 · 폭 기억(RECT.L · RECT.W) → 누른 쪽 사분면 · 없는 옵션 안내 - CIRCLE: 3P · 2P · 지름(D) <2r> · 반지름 기억(CIRCLERAD · 처음 <> 없음) · 한 번 뒤 끝 - POLYGON(새 polygon-tool): 변 수 기억 <4> · 3~1024 정수 · 모서리(E) · 내접/외접 기억 · 숫자 = 아래 변 수평 - ARC(새 arc-tool): 세 점 · 중심(C) · 각도(A) · 끝(E) · 반지름(R) - ELLIPSE(새 ellipse-tool): 축 두 끝 · 중심(C) · 회전(R) · 다른 축 숫자 = 중심에서 반 길이(N10) - SPLINE: 다음 점 입력 · U · 넷째 점부터 C · Esc 버림 · DONUT 중심 되풀이(loopFrom) - 시험 draw-acad 22 · keyboard-chain 옛 선 · 폴리선 줄 두 곳 새 동작으로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
492 lines
16 KiB
TypeScript
492 lines
16 KiB
TypeScript
/**
|
||
* 337-3a 나-1 그리기 8 — LINE · PLINE · RECTANG · CIRCLE · POLYGON · ARC · ELLIPSE · SPLINE 를 오토캐드 차례로.
|
||
* 설계 `docs/raw/조사/2026-10-15_337_CAD오토캐드조작_설계.md` 1-1 · 6-3 나 줄.
|
||
* 실행: cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad draw-acad
|
||
*/
|
||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||
|
||
vi.mock("react-toastify", () => ({
|
||
toast: Object.assign(vi.fn(), { error: vi.fn(), info: vi.fn(), warn: vi.fn(), success: vi.fn() }),
|
||
}));
|
||
|
||
import { remember } from "../../../B07_DesignDetail/openwebcad/src/commands/prompt";
|
||
import { Tool } from "../../../B07_DesignDetail/openwebcad/src/tools";
|
||
import {
|
||
activeTool,
|
||
click,
|
||
entities,
|
||
initIdle,
|
||
key,
|
||
move,
|
||
noticeLine,
|
||
promptLine,
|
||
type,
|
||
typed,
|
||
} from "./helpers/cad-drive";
|
||
|
||
type Xy = { x: number; y: number };
|
||
type Ends = { getStartPoint(): Xy; getEndPoint(): Xy };
|
||
|
||
const enter = () => key("Enter");
|
||
const esc = () => key("Escape");
|
||
const expectXy = (point: Xy, x: number, y: number) => {
|
||
expect(point.x).toBeCloseTo(x, 3);
|
||
expect(point.y).toBeCloseTo(y, 3);
|
||
};
|
||
const lines = () => entities("Line") as unknown as Ends[];
|
||
const expectLine = (line: Ends, a: [number, number], b: [number, number]) => {
|
||
expectXy(line.getStartPoint(), ...a);
|
||
expectXy(line.getEndPoint(), ...b);
|
||
};
|
||
|
||
beforeEach(() => {
|
||
initIdle();
|
||
for (const name of ["LINE.END", "CIRCLERAD", "RECT.L", "RECT.W", "POLYSIDES", "POLYGON.IC"]) {
|
||
remember(name, undefined); // 세션 기억 — 시험끼리 안 섞임
|
||
}
|
||
});
|
||
|
||
describe("LINE", () => {
|
||
it("U = 마지막 토막 · 그 시작점에서 이음 · 그린 토막 없으면 첫 점부터", () => {
|
||
type("l");
|
||
expect(promptLine()).toBe("첫 번째 점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [명령 취소(U)]:");
|
||
type("10,0");
|
||
type("10,10");
|
||
type("u");
|
||
expect(lines()).toHaveLength(1);
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [명령 취소(U)]:");
|
||
type("20,0");
|
||
expect(lines()).toHaveLength(2);
|
||
expectLine(lines()[1], [10, 0], [20, 0]);
|
||
type("u");
|
||
type("u");
|
||
type("u");
|
||
expect(lines()).toHaveLength(0);
|
||
expect(promptLine()).toBe("첫 번째 점 지정:");
|
||
expect(activeTool()).toBe(Tool.LINE);
|
||
});
|
||
|
||
it("C 는 둘째 토막부터 — 그 전엔 거절 · 닫고 끝 → 쉼", () => {
|
||
type("l");
|
||
type("0,0");
|
||
type("10,0");
|
||
type("c");
|
||
expect(noticeLine()).toBe("점 또는 옵션 키워드를 요구함.");
|
||
expect(lines()).toHaveLength(1);
|
||
type("10,10");
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [닫기(C)/명령 취소(U)]:");
|
||
type("C");
|
||
expect(lines()).toHaveLength(3);
|
||
expectLine(lines()[2], [10, 10], [0, 0]);
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
});
|
||
|
||
it("Enter = 끝 → 쉼 · Space = 다시 LINE · 첫 점 빈 Enter = 마지막 끝점에서 이음", () => {
|
||
type("l");
|
||
type("0,0");
|
||
type("10,0");
|
||
enter();
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expect(lines()).toHaveLength(1);
|
||
key(" ");
|
||
expect(activeTool()).toBe(Tool.LINE);
|
||
expect(promptLine()).toBe("첫 번째 점 지정:");
|
||
key(" ");
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [명령 취소(U)]:");
|
||
type("10,5");
|
||
expectLine(lines()[1], [10, 0], [10, 5]);
|
||
});
|
||
|
||
it("첫 점 빈 Enter — 그린 것 없으면 아무 일 없음", () => {
|
||
type("l");
|
||
enter();
|
||
expect(activeTool()).toBe(Tool.LINE);
|
||
expect(promptLine()).toBe("첫 번째 점 지정:");
|
||
});
|
||
|
||
it("Esc = 끝 · 그린 토막 남음", () => {
|
||
type("l");
|
||
type("0,0");
|
||
type("10,0");
|
||
type("20,0");
|
||
move(30, 5);
|
||
esc();
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expect(lines()).toHaveLength(2);
|
||
});
|
||
});
|
||
|
||
type Box = { xmin: number; ymin: number; xmax: number; ymax: number };
|
||
type Shape = { getBoundingBox(): Box; getType(): string };
|
||
type Poly = Shape & { getEntities(): (Shape & Ends)[] };
|
||
const expectBox = (shape: Shape, xmin: number, ymin: number, xmax: number, ymax: number) => {
|
||
const box = shape.getBoundingBox();
|
||
expect([box.xmin, box.ymin, box.xmax, box.ymax].map((v) => Number(v.toFixed(3)))).toEqual([
|
||
xmin,
|
||
ymin,
|
||
xmax,
|
||
ymax,
|
||
]);
|
||
};
|
||
const polys = () => entities("PolyLine") as unknown as Poly[];
|
||
const arcShape = (arc: Shape) =>
|
||
(arc as unknown as { getShape(): { center: Xy; r: number } }).getShape();
|
||
|
||
describe("PLINE", () => {
|
||
it("L = 앞 방향 길이 · U · A 접선 호 · L 선 · 호 뒤 길이 = 접선 · C 닫기", () => {
|
||
type("pline");
|
||
expect(promptLine()).toBe("시작점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [호(A)/길이(L)/명령 취소(U)]:");
|
||
type("10,0");
|
||
expect(promptLine()).toBe("다음 점 지정 또는 [호(A)/닫기(C)/길이(L)/명령 취소(U)]:");
|
||
type("l");
|
||
expect(promptLine()).toBe("선의 길이 지정:");
|
||
type("5");
|
||
type("u");
|
||
type("a");
|
||
expect(promptLine()).toBe("호의 끝점 지정 또는 [닫기(CL)/선(L)/명령 취소(U)]:");
|
||
type("20,10");
|
||
type("l");
|
||
type("L");
|
||
type("5");
|
||
type("c");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expect(polys()).toHaveLength(1);
|
||
const parts = polys()[0].getEntities();
|
||
expect(parts.map((part) => part.getType())).toEqual(["Line", "Arc", "Line", "Line"]);
|
||
expectLine(parts[0], [0, 0], [10, 0]);
|
||
expectXy(arcShape(parts[1]).center, 10, 10); // 동쪽 접선 → 중심 (10, 10)
|
||
expectLine(parts[2], [20, 10], [20, 15]); // 호 끝 접선 = 북쪽
|
||
expectLine(parts[3], [20, 15], [0, 0]);
|
||
});
|
||
|
||
it("첫 토막 호 = 동쪽 접선 · CL = 호로 닫음 · 호 모드 C 는 거절", () => {
|
||
type("pline");
|
||
type("0,0");
|
||
type("a");
|
||
expect(promptLine()).toBe("호의 끝점 지정 또는 [선(L)/명령 취소(U)]:");
|
||
type("10,10");
|
||
type("c");
|
||
expect(noticeLine()).toBe("점 또는 옵션 키워드를 요구함.");
|
||
type("cl");
|
||
const parts = polys()[0].getEntities();
|
||
expect(parts.map((part) => part.getType())).toEqual(["Arc", "Arc"]);
|
||
expectXy(arcShape(parts[1]).center, 0, 10);
|
||
expectXy(parts[1].getEndPoint(), 0, 0);
|
||
});
|
||
|
||
it("Esc = 그린 데까지 남김 · 시작점만이면 없음 · 빈 Enter 끝 · 시작점 Enter = 마지막 끝점", () => {
|
||
type("pline");
|
||
type("0,0");
|
||
type("10,0");
|
||
type("20,5");
|
||
move(40, 40);
|
||
esc();
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expect(polys()).toHaveLength(1);
|
||
expect(polys()[0].getEntities()).toHaveLength(2);
|
||
type("pline");
|
||
type("1,1");
|
||
esc();
|
||
expect(polys()).toHaveLength(1);
|
||
type("pline");
|
||
enter();
|
||
type("@0,10");
|
||
enter();
|
||
expect(polys()).toHaveLength(2);
|
||
expectLine(polys()[1].getEntities()[0], [20, 5], [20, 15]);
|
||
});
|
||
});
|
||
|
||
describe("RECTANG", () => {
|
||
it("D = 길이 · 폭(기억) → 누른 쪽 사분면 · 없는 옵션 안내 · 하나 뒤 끝", () => {
|
||
type("rec");
|
||
expect(promptLine()).toBe("첫 번째 구석점 지정:");
|
||
type("e");
|
||
expect(noticeLine()).toBe("「고도(E)」 옵션은 이 CAD 에 없음.");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("다른 구석점 지정 또는 [치수(D)]:");
|
||
type("d");
|
||
expect(promptLine()).toBe("직사각형의 길이 지정 <10.0000>:");
|
||
type("30");
|
||
expect(promptLine()).toBe("직사각형의 폭 지정 <10.0000>:");
|
||
type("20");
|
||
expect(promptLine()).toBe("다른 구석점 지정 또는 [치수(D)]:");
|
||
type("-1,-1");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expectBox(entities("Rectangle")[0], -30, -20, 0, 0);
|
||
key(" ");
|
||
type("100,100");
|
||
type("d");
|
||
expect(promptLine()).toBe("직사각형의 길이 지정 <30.0000>:");
|
||
enter();
|
||
expect(promptLine()).toBe("직사각형의 폭 지정 <20.0000>:");
|
||
enter();
|
||
type("101,99");
|
||
expectBox(entities("Rectangle")[1], 100, 80, 130, 100);
|
||
});
|
||
|
||
it("두 구석 · Esc 첫 구석 = 끝", () => {
|
||
type("rec");
|
||
esc();
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
type("rec");
|
||
type("0,0");
|
||
type("@5,3");
|
||
expectBox(entities("Rectangle")[0], 0, 0, 5, 3);
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
});
|
||
});
|
||
|
||
describe("CIRCLE", () => {
|
||
const radius = (index: number) =>
|
||
(entities("Circle")[index] as unknown as { getRadius(): number }).getRadius();
|
||
|
||
it("반지름 · 처음엔 <> 없음(Enter 아무 일 없음) · 기억 · 한 번 뒤 끝 · 음수 거절 · Ttr 없음", () => {
|
||
type("c");
|
||
expect(promptLine()).toBe("원에 대한 중심점 지정 또는 [3점(3P)/2점(2P)]:");
|
||
type("t");
|
||
expect(noticeLine()).toBe("「Ttr - 접선 접선 반지름(T)」 옵션은 이 CAD 에 없음.");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("원의 반지름 지정 또는 [지름(D)]:");
|
||
enter();
|
||
expect(entities("Circle")).toHaveLength(0);
|
||
type("-5");
|
||
expect(noticeLine()).toBe("값은 양수이고 0이 아니어야 함.");
|
||
type("5");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expect(radius(0)).toBeCloseTo(5);
|
||
key(" ");
|
||
type("20,0");
|
||
expect(promptLine()).toBe("원의 반지름 지정 또는 [지름(D)] <5.0000>:");
|
||
enter();
|
||
expect(radius(1)).toBeCloseTo(5);
|
||
expectBox(entities("Circle")[1], 15, -5, 25, 5);
|
||
});
|
||
|
||
it("D 지름 <2r> · 누른 점 반지름도 기억", () => {
|
||
remember("CIRCLERAD", 3);
|
||
type("c");
|
||
type("0,0");
|
||
type("d");
|
||
expect(promptLine()).toBe("원의 지름 지정 <6.0000>:");
|
||
type("8");
|
||
expect(radius(0)).toBeCloseTo(4);
|
||
type("c");
|
||
type("50,50");
|
||
expect(promptLine()).toBe("원의 반지름 지정 또는 [지름(D)] <4.0000>:");
|
||
type("@0,7");
|
||
expect(radius(1)).toBeCloseTo(7);
|
||
type("c");
|
||
type("90,90");
|
||
expect(promptLine()).toBe("원의 반지름 지정 또는 [지름(D)] <7.0000>:");
|
||
});
|
||
|
||
it("3P · 2P", () => {
|
||
type("c");
|
||
type("3p");
|
||
expect(promptLine()).toBe("원 위의 첫 번째 점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("원 위의 두 번째 점 지정:");
|
||
type("10,0");
|
||
expect(promptLine()).toBe("원 위의 세 번째 점 지정:");
|
||
type("5,5");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expectBox(entities("Circle")[0], 0, -5, 10, 5);
|
||
type("c");
|
||
type("2P");
|
||
expect(promptLine()).toBe("원 지름의 첫 번째 끝점 지정:");
|
||
type("20,0");
|
||
expect(promptLine()).toBe("원 지름의 두 번째 끝점 지정:");
|
||
type("40,0");
|
||
expectBox(entities("Circle")[1], 20, -10, 40, 10);
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
});
|
||
});
|
||
|
||
/** 폴리선 꼭짓점(소수 셋) */
|
||
const corners = (poly: Poly) =>
|
||
poly.getEntities().map((part) => {
|
||
const point = part.getStartPoint();
|
||
return [Number(point.x.toFixed(3)) + 0, Number(point.y.toFixed(3)) + 0];
|
||
});
|
||
|
||
describe("POLYGON", () => {
|
||
it("변 수 <4> · 2.5 · 2 거절 · 외접 숫자 = 아래 변 수평 · 기억 · 한 번 뒤 끝", () => {
|
||
type("pol");
|
||
expect(promptLine()).toBe("변의 수 입력 <4>:");
|
||
type("2.5");
|
||
expect(noticeLine()).toBe("정수 값을 요구함.");
|
||
type("2");
|
||
expect(noticeLine()).toBe("3에서 1024 사이의 정수를 요구함.");
|
||
type("6");
|
||
expect(promptLine()).toBe("폴리곤의 중심 지정 또는 [모서리(E)]:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("옵션 입력 [원에 내접(I)/원에 외접(C)] <I>:");
|
||
type("c");
|
||
expect(promptLine()).toBe("원의 반지름 지정:");
|
||
type("10");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
const hexagon = polys()[0];
|
||
expect(hexagon.getEntities()).toHaveLength(6);
|
||
expect(hexagon.getBoundingBox().ymin).toBeCloseTo(-10); // 아래 변 = 반지름 거리 · 수평
|
||
expect(corners(hexagon).filter(([, y]) => y === -10)).toHaveLength(2);
|
||
key(" ");
|
||
expect(promptLine()).toBe("변의 수 입력 <6>:");
|
||
enter();
|
||
type("100,0");
|
||
expect(promptLine()).toBe("옵션 입력 [원에 내접(I)/원에 외접(C)] <C>:");
|
||
type("i");
|
||
type("10");
|
||
const inscribed = polys()[1];
|
||
expect(corners(inscribed).filter(([, y]) => y === -8.66)).toHaveLength(2); // 내접 · 아래 변 수평(꼭짓점 −60° · −120°)
|
||
expect(inscribed.getBoundingBox().xmax).toBeCloseTo(110);
|
||
});
|
||
|
||
it("점 = 크기 + 돌림(내접 꼭짓점) · E 모서리 = 왼쪽(반시계)", () => {
|
||
type("pol");
|
||
type("4");
|
||
type("0,0");
|
||
enter(); // <I>
|
||
type("0,10");
|
||
expect(corners(polys()[0])).toContainEqual([0, 10]);
|
||
type("pol");
|
||
enter();
|
||
type("e");
|
||
expect(promptLine()).toBe("모서리의 첫 번째 끝점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("모서리의 두 번째 끝점 지정:");
|
||
type("10,0");
|
||
expectBox(polys()[1], 0, 0, 10, 10);
|
||
});
|
||
});
|
||
|
||
describe("ARC", () => {
|
||
it("세 점 · 한 번 뒤 끝", () => {
|
||
type("a");
|
||
expect(promptLine()).toBe("호의 시작점 지정:");
|
||
type("10,0");
|
||
expect(promptLine()).toBe("호의 두 번째 점 지정 또는 [중심(C)/끝(E)]:");
|
||
type("0,10");
|
||
expect(promptLine()).toBe("호의 끝점 지정:");
|
||
type("-10,0");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expectBox(entities("Arc")[0], -10, 0, 10, 10);
|
||
});
|
||
|
||
it("C 중심 → 끝점 · A 사이각(− = 시계)", () => {
|
||
type("a");
|
||
type("10,0");
|
||
type("c");
|
||
expect(promptLine()).toBe("호의 중심점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("호의 끝점 지정 또는 [각도(A)]:");
|
||
type("0,5");
|
||
expectBox(entities("Arc")[0], 0, 0, 10, 10);
|
||
type("a");
|
||
type("10,0");
|
||
type("c");
|
||
type("0,0");
|
||
type("a");
|
||
expect(promptLine()).toBe("사이각 지정:");
|
||
type("-90");
|
||
expectBox(entities("Arc")[1], 0, -10, 10, 0);
|
||
});
|
||
|
||
it("E 끝 → 중심 · R 반지름 · 시작점 Enter 없음", () => {
|
||
type("a");
|
||
enter();
|
||
expect(promptLine()).toBe("호의 시작점 지정:");
|
||
type("10,0");
|
||
type("e");
|
||
expect(promptLine()).toBe("호의 끝점 지정:");
|
||
type("-10,0");
|
||
expect(promptLine()).toBe("호의 중심점 지정 또는 [반지름(R)]:");
|
||
type("0,0");
|
||
expectBox(entities("Arc")[0], -10, 0, 10, 10);
|
||
type("a");
|
||
type("10,0");
|
||
type("e");
|
||
type("0,10");
|
||
type("r");
|
||
expect(promptLine()).toBe("호의 반지름 지정:");
|
||
type("10");
|
||
expect(arcShape(entities("Arc")[1]).r).toBeCloseTo(10);
|
||
expectXy(arcShape(entities("Arc")[1]).center, 0, 0);
|
||
});
|
||
});
|
||
|
||
describe("ELLIPSE", () => {
|
||
it("축 두 끝 · 다른 축 거리 숫자 = 중심에서 반 길이(N10) · 호 없음", () => {
|
||
type("el");
|
||
expect(promptLine()).toBe("타원의 축 끝점 지정 또는 [중심(C)]:");
|
||
type("a");
|
||
expect(noticeLine()).toBe("「호(A)」 옵션은 이 CAD 에 없음.");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("축의 다른 끝점 지정:");
|
||
type("20,0");
|
||
expect(promptLine()).toBe("다른 축으로 거리를 지정 또는 [회전(R)]:");
|
||
move(10, 30);
|
||
type("5");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
expectBox(polys()[0], 0, -5, 20, 5);
|
||
});
|
||
|
||
it("C 중심 · R 회전 60° = 축 × cos", () => {
|
||
type("el");
|
||
type("c");
|
||
expect(promptLine()).toBe("타원의 중심 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("축의 끝점 지정:");
|
||
type("10,0");
|
||
type("r");
|
||
expect(promptLine()).toBe("주축 둘레로 회전 지정:");
|
||
type("60");
|
||
expectBox(polys()[0], -10, -5, 10, 5);
|
||
});
|
||
});
|
||
|
||
describe("SPLINE", () => {
|
||
it("물음 차례 · U · 넷째 점부터 C(닫고 끝)", () => {
|
||
type("spl");
|
||
expect(promptLine()).toBe("첫 번째 점 지정:");
|
||
type("0,0");
|
||
expect(promptLine()).toBe("다음 점 입력:");
|
||
type("10,0");
|
||
expect(promptLine()).toBe("다음 점 입력 또는 [명령 취소(U)]:");
|
||
type("c");
|
||
expect(noticeLine()).toBe("점 또는 옵션 키워드를 요구함.");
|
||
type("10,10");
|
||
expect(promptLine()).toBe("다음 점 입력 또는 [명령 취소(U)/닫기(C)]:");
|
||
type("u");
|
||
expect(promptLine()).toBe("다음 점 입력 또는 [명령 취소(U)]:");
|
||
type("10,10");
|
||
type("0,10");
|
||
type("c");
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
const parts = polys()[0].getEntities();
|
||
expectXy(parts[parts.length - 1].getEndPoint(), 0, 0);
|
||
expectXy(parts[0].getStartPoint(), 0, 0);
|
||
});
|
||
|
||
it("빈 Enter = 확정 · Esc = 버림", () => {
|
||
type("spl");
|
||
type("0,0");
|
||
type("10,0");
|
||
type("20,5");
|
||
enter();
|
||
expect(polys()).toHaveLength(1);
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
type("spl");
|
||
type("0,0");
|
||
type("10,0");
|
||
type("20,5");
|
||
esc();
|
||
expect(polys()).toHaveLength(1);
|
||
expect(activeTool()).toBe(Tool.SELECT);
|
||
});
|
||
});
|