# -*- coding: utf-8 -*- """스프레드시트 D 입력 — 고름 · 입력 글 규칙 · 참조 가리키기 자리 · 채우기 끌기 목표(엑셀 동작 기준). Node 도우미(helper_spreadsheet_d.cjs)가 TS 를 풀어 돌리고 JSON 을 줌 — 남의 몫이 빈 몸이면 가짜 부품. 화면 조작(한글 첫 글자 · 수식 입력줄 · 끌기)은 harness_spreadsheet_d.html 을 ORCA 로. """ import json import os import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) @pytest.fixture(scope="module") def r(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_spreadsheet_d.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_merge_selection(r): assert r["mergeClick"] == "C4|C4:F4" assert r["mergeMove"] == ["G4|G4", "C4|C4:F4", "B4|B4"] assert r["colHead"] == "A:H" # 걸친 병합(A1:H1 · C3:F3 · A6:G6)만큼 늘림 def test_shift_ctrl_arrows(r): assert r["shiftExtend"] == ["A4|A4:B4", "A4|A4:F4", "A4|A4:B4", "A4|A4:B5"] assert r["ctrlJump"] == ["A6|A6:G6", "A1048576|A1048576", "A6|A6:G6"] assert r["hiddenSkip"] == ["K1"] assert r["usedEnd"] == "J6" def test_cycle_in_selection(r): assert r["cycleEnter"] == ["K12", "L11", "L12", "K11"] assert r["cycleTab"] == ["L11", "K12", "L12"] assert r["cycleSingle"] is None def test_text_to_cell(r): got = r["textToCell"] assert [c.get("값", c.get("식")) for c in got] == [ 12, 1234.5, 0.5, -1000, True, "=12", "=", "A1+1", None, "1,23", "abc", ] assert "식" in got[7] and all(c["서식"] == 4 for c in got) assert r["textToCellNoStyle"] is None def test_cell_to_text(r): assert r["cellToText"] == ["'=12", "'12", "abc", "1.5", "FALSE", "=A1*2", ""] def test_pointing(r): assert r["insertable"] == [True, True, False, True, True, True, False, False] assert r["highlights"] == ["s1:A1:0", "s1:B2:C3:1", "s1:A1:0", "s2:B1:2"] def test_fill_target(r): assert r["fill"] == [ ["B2:C10", None], ["B2:G4", None], ["B2:B4", "C2:C4"], ["B2:C3", "B4:C4"], ] def test_formula_hint(r): assert r["hint"] == [ {"prefix": "su"}, {"prefix": "ro"}, None, {"func": "ROUND", "arg": 1}, {"func": "SUM", "arg": 2}, {"func": "IF", "arg": 1}, None, ] def test_abs_cycle_and_fold(r): assert r["abs"] == [ "=$A$1+B2", "=A$1+B2", "=$A1+B2", "=A1+B2", "=SUM(Sheet2!$A$1:$B$3)|21", None, None, ] assert r["foldSkip"] == "L5" def test_links_follow_rows_cols(r): # 행 넣기 → 밀림 · 연결 칸 열 지움 → 빠짐 · 되돌리기 → 제자리 assert r["links"] == ["L:B7,W:D5", "L:B7", "L:B7,W:D5", "L:B5,W:D3", "D1"] def test_links_bar_sits_under_sheet(): # 알약 막대 = 시트 아래 · 상태줄 위 · 자재 고르기 창은 막대 위로 엶 with open( os.path.join(HERE, "../../../A00_Common/spreadsheet/spreadsheet_links.ts"), encoding="utf-8" ) as f: src = f.read() assert 'insertBefore(bar, ctx.root.querySelector(":scope > .ss-statusbar"))' in src assert "pick.style.bottom = `${o.bottom - bar.getBoundingClientRect().top + 4}px`" in src def test_typed_format(r): assert r["typedFormat"] == ["0%", "0.0%", "#,##0.0", "#,##0", None, None, None] def test_upper_formula(r): assert r["upperFormula"] == [ "SUM(A1:B2)+ROUND(C3,0)", "'시트 a'!A1*TRUE", '"abc"&A1', "내이름*2", ] def test_close_parens(r): assert r["closeParens"] == ["=(1+2)", "=SUM(1,ROUND(2,0))", None, None, '=")"&(1)'] def test_spill(r): sp = r["spill"] assert sp["left"] is None # 오른 칸(3)에 값 → 못 넘침 assert sp["leftFree"] == [0, 100, 2, 4, 0, 0] # 빈 칸 둘 덮음 assert sp["right"] == [100, 0, 0, 2, 0, 0] assert sp["center"] == [40, 40, 1, 3, 0, 0] assert sp["centerBlocked"] == [40, 40, 1, 2, 0, 40] # 오른쪽 막힘 → 오른쪽 반 잘라 냄 assert sp["fits"] is None assert sp["edge"] is None # 판 끝(그린 칸 없음)