개요
contenteditable 에디터 테스트는 브라우저 차이, IME 동작, DOM 복잡성으로 인해 어렵습니다. 이 가이드는 다양한 수준의 테스트 전략을 다룹니다.
테스트 도전과제
contenteditable 에디터 테스트의 주요 도전과제:
- 이벤트 발생 및 DOM 동작의 브라우저 차이
- IME 조합 이벤트가 브라우저 간 일관되게 발생하지 않음
- 브라우저 간 선택 API 차이
- 모바일 브라우저가 데스크톱과 다른 동작
- 비동기 작업 및 DOM 업데이트의 타이밍 문제
단위 테스트
모델 작업과 변환을 격리하여 테스트합니다.
모델 테스트
import { describe, it, expect } from 'vitest';
import { Editor } from './editor';
describe('모델 작업', () => {
it('커서 위치에 텍스트 삽입', () => {
const editor = new Editor();
editor.setContent('Hello world');
editor.setSelection({ start: 5, end: 5 });
editor.insertText(' beautiful');
expect(editor.getContent()).toBe('Hello beautiful world');
});
it('선택된 텍스트 삭제', () => {
const editor = new Editor();
editor.setContent('Hello world');
editor.setSelection({ start: 0, end: 5 });
editor.deleteContent();
expect(editor.getContent()).toBe(' world');
});
});작업 테스트
describe('작업', () => {
it('삽입 작업 적용', () => {
const doc = { type: 'doc', children: [] };
const operation = {
type: 'insert',
path: [0],
node: { type: 'text', text: 'Hello' }
};
const result = applyOperation(doc, operation);
expect(result.children[0].text).toBe('Hello');
});
it('실행 취소/다시 실행 지원', () => {
const editor = new Editor();
editor.insertText('Hello');
editor.insertText(' world');
editor.undo();
expect(editor.getContent()).toBe('Hello');
editor.redo();
expect(editor.getContent()).toBe('Hello world');
});
});통합 테스트
DOM 동기화 및 이벤트 처리를 테스트합니다.
DOM 테스트
import { render, screen, fireEvent } from '@testing-library/react';
describe('DOM 동기화', () => {
it('모델 변경 시 DOM 업데이트', () => {
const { container } = render(<Editor />);
const editor = container.querySelector('[contenteditable]');
// 입력 시뮬레이션
fireEvent.input(editor, {
target: { textContent: 'Hello world' }
});
expect(editor.textContent).toBe('Hello world');
});
});이벤트 테스트
describe('이벤트 처리', () => {
it('beforeinput 이벤트 처리', () => {
const editor = new Editor();
const element = editor.element;
const handler = vi.fn();
editor.on('beforeinput', handler);
const event = new InputEvent('beforeinput', {
inputType: 'insertText',
data: 'Hello'
});
element.dispatchEvent(event);
expect(handler).toHaveBeenCalled();
});
});IME 테스트
IME 테스트는 브라우저 차이로 인해 특별한 처리가 필요합니다.
조합 테스트
describe('IME 조합', () => {
it('조합 이벤트 처리', async () => {
const editor = new Editor();
const element = editor.element;
// 조합 시뮬레이션
element.dispatchEvent(new CompositionEvent('compositionstart'));
element.dispatchEvent(new CompositionEvent('compositionupdate', { data: '한' }));
element.dispatchEvent(new CompositionEvent('compositionupdate', { data: '한글' }));
element.dispatchEvent(new CompositionEvent('compositionend', { data: '한글' }));
await new Promise(resolve => setTimeout(resolve, 100));
expect(editor.getContent()).toContain('한글');
});
});크로스 브라우저 IME 테스트
참고: iOS Safari는 한국어 IME에 대해 조합 이벤트를 발생시키지 않습니다. 실제 기기나 브라우저 자동화 도구로 테스트하세요.
엔드투엔드 테스트
브라우저 자동화로 전체 사용자 워크플로우를 테스트합니다.
Playwright 테스트
import { test, expect } from '@playwright/test';
test('텍스트 삽입', async ({ page }) => {
await page.goto('/editor');
const editor = page.locator('[contenteditable]');
await editor.click();
await editor.type('Hello world');
await expect(editor).toHaveText('Hello world');
});
test('IME 조합 처리', async ({ page }) => {
await page.goto('/editor');
const editor = page.locator('[contenteditable]');
await editor.click();
// 한국어 문자 입력
await editor.pressSequentially('한글');
await expect(editor).toContainText('한글');
});모바일 테스트
모바일 테스트는 실제 기기나 에뮬레이션이 필요합니다.
test('가상 키보드 처리', async ({ page, isMobile }) => {
if (!isMobile) test.skip();
await page.goto('/editor');
const editor = page.locator('[contenteditable]');
await editor.tap();
// 가상 키보드가 나타나야 함
await editor.type('Hello');
await expect(editor).toHaveText('Hello');
});성능 테스트
대용량 문서로 에디터 성능을 테스트합니다.
test('대용량 문서 처리', async () => {
const editor = new Editor();
const largeText = 'Hello '.repeat(10000);
const start = performance.now();
editor.setContent(largeText);
const end = performance.now();
expect(end - start).toBeLessThan(100); // 100ms 미만으로 완료되어야 함
});모범 사례
- 모델 작업을 격리하여 테스트
- DOM 동기화를 별도로 테스트
- 조합 테스트에 실제 IME 사용
- 여러 브라우저에서 테스트
- 실제 모바일 기기에서 테스트
- 외부 종속성 모킹
- 엣지 케이스 및 오류 조건 테스트