dogfood: 설정 페이지의 각 section 이 너무 단답형이고 도움말 텍스트도 기술 용어 (rebase, fast-forward, NTP) 위주라 불친절. - 공통 SectionIntro 컴포넌트 신설 (12px gray paragraph, margin-bottom 12). - 6 section (AI 제공자 / Vision / 자동실행 / 백업 / 동기화 / 정보) 상단에 "이게 뭐고 왜 필요한지" 1-2 문장 안내 추가. 톤은 담백 + 업무적 (존댓말, Inkling 1인칭). - SyncHelpModal section 1, 2, 3 의 기술 용어를 사용자 언어로 풀어쓰기. "fetch + rebase" → "원격 변경 먼저 받아오기", "NTP" → "기기 시각 어긋남", "non-fast-forward push 거부" → "업로드 거부 시 자동 재시도" 등. 시각/레이아웃은 그대로 유지 — 텍스트 변경만. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
2.7 KiB
TypeScript
66 lines
2.7 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import '@testing-library/jest-dom/vitest';
|
|
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
|
|
import React from 'react';
|
|
import { SyncHelpModal } from '../../src/renderer/inbox/components/SyncHelpModal';
|
|
|
|
describe('SyncHelpModal', () => {
|
|
beforeEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
it('4 섹션 헤더 렌더링', () => {
|
|
render(<SyncHelpModal onClose={() => {}} />);
|
|
expect(screen.getByRole('heading', { name: /충돌 해결/ })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: /자동으로 처리되는 일/ })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: /모르고 넘어가기 쉬운 함정/ })).toBeInTheDocument();
|
|
expect(screen.getByRole('heading', { name: /Setup/ })).toBeInTheDocument();
|
|
});
|
|
|
|
it('각 섹션이 anchor id 보유', () => {
|
|
const { container } = render(<SyncHelpModal onClose={() => {}} />);
|
|
expect(container.querySelector('#main-conflict')).not.toBeNull();
|
|
expect(container.querySelector('#auto')).not.toBeNull();
|
|
expect(container.querySelector('#silent')).not.toBeNull();
|
|
expect(container.querySelector('#setup')).not.toBeNull();
|
|
});
|
|
|
|
it('초기 anchor prop 으로 해당 섹션 scrollIntoView 호출', () => {
|
|
const scrollSpy = vi.fn();
|
|
Element.prototype.scrollIntoView = scrollSpy;
|
|
render(<SyncHelpModal onClose={() => {}} initialAnchor="main-conflict" />);
|
|
expect(scrollSpy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('X 버튼 클릭 → onClose 호출', () => {
|
|
const onClose = vi.fn();
|
|
render(<SyncHelpModal onClose={onClose} />);
|
|
fireEvent.click(screen.getByRole('button', { name: /닫기/ }));
|
|
expect(onClose).toHaveBeenCalled();
|
|
});
|
|
|
|
it('overlay 클릭 → onClose 호출', () => {
|
|
const onClose = vi.fn();
|
|
const { container } = render(<SyncHelpModal onClose={onClose} />);
|
|
const overlay = container.firstChild as HTMLElement;
|
|
fireEvent.click(overlay);
|
|
expect(onClose).toHaveBeenCalled();
|
|
});
|
|
|
|
it('modal body 클릭 → onClose 호출 X (stopPropagation)', () => {
|
|
const onClose = vi.fn();
|
|
render(<SyncHelpModal onClose={onClose} />);
|
|
fireEvent.click(screen.getByRole('heading', { name: /충돌 해결/ }));
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('주요 시나리오 키워드 본문 포함 (회귀)', () => {
|
|
render(<SyncHelpModal onClose={() => {}} />);
|
|
expect(screen.getByText(/편집\/편집/)).toBeInTheDocument();
|
|
expect(screen.getByText(/삭제\/편집/)).toBeInTheDocument();
|
|
expect(screen.getByText(/AI 결과 충돌/)).toBeInTheDocument();
|
|
expect(screen.getByText(/git@https:\/\//)).toBeInTheDocument();
|
|
});
|
|
});
|