feat: release Find-Word v0.1

This commit is contained in:
oz
2026-10-14 06:19:24 +00:00
parent 28ee2ce498
commit 1c0069c367
258 changed files with 21509 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 3 passed (3)
Start at 04:54:59
Duration 835ms (environment 78%, setup 13%, transform 6%, tests 1%, worker 1%)
+72
View File
@@ -0,0 +1,72 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/content.test.ts (3 tests | 3 failed) 12ms
❯ 저장된 문제·환경 데이터 (3)
× 서로 다른 질문 의도를 가진 열 가지 이상 문장 패턴을 제공한다 5ms
× 네 개의 고유 사전 단어와 서로 다른 오답 규칙을 제공한다 1ms
× 여덟 환경과 단조롭게 낮아지는 다섯 상대 dB를 제공한다 5ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/content.test.ts > 저장된 문제·환경 데이터 > 서로 다른 질문 의도를 가진 열 가지 이상 문장 패턴을 제공한다
AssertionError: expected 0 to be greater than or equal to 10
❯ tests/content.test.ts:8:29
6| describe('저장된 문제·환경 데이터', () => {
7| it('서로 다른 질문 의도를 가진 열 가지 이상 문장 패턴을 제공한다', () => {
8| expect(PATTERNS.length).toBeGreaterThanOrEqual(10);
| ^
9| expect(new Set(PATTERNS.map(p => p.id)).size).toBe(PATTERNS.length…
10| expect(new Set(PATTERNS.map(p => p.question)).size).toBe(PATTERNS.…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯
FAIL tests/content.test.ts > 저장된 문제·환경 데이터 > 네 개의 고유 사전 단어와 서로 다른 오답 규칙을 제공한다
AssertionError: expected 0 to be greater than or equal to 10
❯ tests/content.test.ts:14:26
12| });
13| it('네 개의 고유 사전 단어와 서로 다른 오답 규칙을 제공한다', () => {
14| expect(WORDS.length).toBeGreaterThanOrEqual(10);
| ^
15| WORDS.forEach(entry => {
16| expect(entry.distractors).toHaveLength(3);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯
FAIL tests/content.test.ts > 저장된 문제·환경 데이터 > 여덟 환경과 단조롭게 낮아지는 다섯 상대 dB를 제공한다
AssertionError: expected [] to deeply equal [ '길거리', '쇼핑몰', '극장', '백화점', …(4) ]
- Expected
+ Received
- [
- "길거리",
- "쇼핑몰",
- "극장",
- "백화점",
- "시장",
- "지하철",
- "카페",
- "비 오는 거리",
- ]
+ []
❯ tests/content.test.ts:36:43
34| });
35| it('여덟 환경과 단조롭게 낮아지는 다섯 상대 dB를 제공한다', () => {
36| expect(ENVIRONMENTS.map(e => e.name)).toEqual(['길거리', '쇼핑몰', '극장',…
| ^
37| expect(new Set(ENVIRONMENTS.map(e => e.seed)).size).toBe(8);
38| expect(LEVELS).toHaveLength(5);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯
Test Files 1 failed (1)
Tests 3 failed (3)
Start at 04:54:05
Duration 899ms (environment 78%, setup 13%, transform 6%, tests 2%, worker 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/game.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 6 passed (6)
Start at 04:56:41
Duration 875ms (environment 75%, setup 13%, transform 5%, tests 5%, worker 1%, import 1%)
+105
View File
@@ -0,0 +1,105 @@
> find-word@0.1.0 test
> vitest run tests/game.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/game.test.ts (6 tests | 6 failed) 14ms
❯ 출제와 정답 판정 (3)
× 열 문제에 모든 유형·다섯 난이도·두 응답 방식을 섞는다 6ms
× 선택한 응답 방식과 환경을 모든 문제에 적용한다 1ms
× 한글 정규형·공백·문장부호만 정리하고 부분 정답은 인정하지 않는다 4ms
❯ 게임 상태·점수·경합 방지 (3)
× 재생 완료 이전에는 답변·다음 문제를 허용하지 않으며 중복 채점을 막는다 1ms
× 오디오 오류 후 재시도와 다시 듣기는 점수를 변경하지 않는다 0ms
× 열 문제를 끝내면 정답률·연속 정답·단계별 결과를 집계한다 0ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 열 문제에 모든 유형·다섯 난이도·두 응답 방식을 섞는다
Error: 출제 미구현
❯ createQuestions src/game.ts:17:106
15| }
16| export type Action = { type: 'PLAY' | 'STARTED' | 'ENDED' | 'NEXT' } |…
17| export function createQuestions(_seed: number, _mode: Mode, _environme…
| ^
18| export function normalizeAnswer(value: string): string { return value;…
19| export function isCorrect(input: string, answer: string): boolean { re…
❯ tests/game.test.ts:8:23
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 선택한 응답 방식과 환경을 모든 문제에 적용한다
Error: 출제 미구현
❯ createQuestions src/game.ts:17:106
15| }
16| export type Action = { type: 'PLAY' | 'STARTED' | 'ENDED' | 'NEXT' } |…
17| export function createQuestions(_seed: number, _mode: Mode, _environme…
| ^
18| export function normalizeAnswer(value: string): string { return value;…
19| export function isCorrect(input: string, answer: string): boolean { re…
❯ tests/game.test.ts:26:12
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯
FAIL tests/game.test.ts > 출제와 정답 판정 > 한글 정규형·공백·문장부호만 정리하고 부분 정답은 인정하지 않는다
AssertionError: expected ' “사 과.” ' to be '사과' // Object.is equality
Expected: "사과"
Received: " “사 과.” "
❯ tests/game.test.ts:30:43
28| });
29| it('한글 정규형·공백·문장부호만 정리하고 부분 정답은 인정하지 않는다', () => {
30| expect(normalizeAnswer(' “사 과.” ')).toBe('사과');
| ^
31| expect(isCorrect('사과'.normalize('NFD'), '사과')).toBe(true);
32| expect(isCorrect('사과 또는 바다', '사과')).toBe(false);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯
FAIL tests/game.test.ts > 게임 상태·점수·경합 방지 > 재생 완료 이전에는 답변·다음 문제를 허용하지 않으며 중복 채점을 막는다
Error: 출제 미구현
❯ createQuestions src/game.ts:17:106
15| }
16| export type Action = { type: 'PLAY' | 'STARTED' | 'ENDED' | 'NEXT' } |…
17| export function createQuestions(_seed: number, _mode: Mode, _environme…
| ^
18| export function normalizeAnswer(value: string): string { return value;…
19| export function isCorrect(input: string, answer: string): boolean { re…
❯ tests/game.test.ts:40:29
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯
FAIL tests/game.test.ts > 게임 상태·점수·경합 방지 > 오디오 오류 후 재시도와 다시 듣기는 점수를 변경하지 않는다
Error: 출제 미구현
❯ createQuestions src/game.ts:17:106
15| }
16| export type Action = { type: 'PLAY' | 'STARTED' | 'ENDED' | 'NEXT' } |…
17| export function createQuestions(_seed: number, _mode: Mode, _environme…
| ^
18| export function normalizeAnswer(value: string): string { return value;…
19| export function isCorrect(input: string, answer: string): boolean { re…
❯ tests/game.test.ts:56:29
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯
FAIL tests/game.test.ts > 게임 상태·점수·경합 방지 > 열 문제를 끝내면 정답률·연속 정답·단계별 결과를 집계한다
Error: 출제 미구현
❯ createQuestions src/game.ts:17:106
15| }
16| export type Action = { type: 'PLAY' | 'STARTED' | 'ENDED' | 'NEXT' } |…
17| export function createQuestions(_seed: number, _mode: Mode, _environme…
| ^
18| export function normalizeAnswer(value: string): string { return value;…
19| export function isCorrect(input: string, answer: string): boolean { re…
❯ tests/game.test.ts:69:29
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯
Test Files 1 failed (1)
Tests 6 failed (6)
Start at 04:56:04
Duration 872ms (environment 78%, setup 13%, transform 6%, tests 2%, worker 1%, import 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/dsp.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 3 passed (3)
Start at 04:58:27
Duration 1.23s (environment 51%, tests 37%, setup 8%, transform 3%, worker 1%)
+37
View File
@@ -0,0 +1,37 @@
> find-word@0.1.0 test
> vitest run tests/dsp.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/dsp.test.ts (3 tests | 1 failed) 82ms
❯ 상대 dB와 결정론적 배경 합성 (3)
× 여덟 환경이 서로 다르고 같은 seed는 같은 안전 범위 신호를 만든다 76ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 여덟 환경이 서로 다르고 같은 seed는 같은 안전 범위 신호를 만든다
AssertionError: expected -0 to be +0 // Object.is equality
- Expected
+ Received
- 0
+ -0
❯ tests/dsp.test.ts:29:25
27| expect(Math.max(...signal.map(Math.abs))).toBeLessThan(1);
28| // 루프 경계에서 불연속 클릭이 생기지 않도록 양 끝을 0으로 감싼다.
29| expect(signal[0]).toBe(0);
| ^
30| expect(signal.at(-1)).toBe(0);
31| signatures.add([...signal.slice(0, 100)].join(','));
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 2 passed (3)
Start at 04:57:34
Duration 912ms (environment 72%, setup 12%, tests 10%, transform 5%, worker 1%, import 1%)
+56
View File
@@ -0,0 +1,56 @@
> find-word@0.1.0 test
> vitest run tests/dsp.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/dsp.test.ts (3 tests | 3 failed) 70ms
❯ 상대 dB와 결정론적 배경 합성 (3)
× 20 log10(음성 gain / 배경 gain)이 표시 dB와 일치한다 5ms
× 평균 전력을 맞추고 원본 배열을 변경하지 않으며 무음은 오류로 알린다 1ms
× 여덟 환경이 서로 다르고 같은 seed는 같은 안전 범위 신호를 만든다 62ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 20 log10(음성 gain / 배경 gain)이 표시 dB와 일치한다
AssertionError: expected +0 to be close to 6, received difference is 6, but expected 5e-11
❯ tests/dsp.test.ts:10:77
8| describe('상대 dB와 결정론적 배경 합성', () => {
9| it('20 log10(음성 gain / 배경 gain)이 표시 dB와 일치한다', () => {
10| for (const level of LEVELS) expect(20 * Math.log10(dbToGain(level.…
| ^
11| expect(dbToGain(-6)).toBeCloseTo(.501187, 5);
12| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 평균 전력을 맞추고 원본 배열을 변경하지 않으며 무음은 오류로 알린다
AssertionError: expected 0.2738612848738597 to be close to 0.12, received difference is 0.1538612848738597, but expected 5e-7
❯ tests/dsp.test.ts:16:25
14| const input = new Float32Array([.4, -.2, .1, -.3]);
15| const output = normalizeRms(input);
16| expect(rms(output)).toBeCloseTo(.12, 6);
| ^
17| expect(output).not.toBe(input);
18| expect(() => normalizeRms(new Float32Array(4))).toThrow('무음');
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯
FAIL tests/dsp.test.ts > 상대 dB와 결정론적 배경 합성 > 여덟 환경이 서로 다르고 같은 seed는 같은 안전 범위 신호를 만든다
AssertionError: expected +0 to be close to 0.12, received difference is 0.12, but expected 0.000005
❯ tests/dsp.test.ts:26:27
24| expect(signal).toHaveLength(16000);
25| expect(signal).toEqual(synthesizeBackground(preset, 16000, 1));
26| expect(rms(signal)).toBeCloseTo(.12, 5);
| ^
27| expect(Math.max(...signal.map(Math.abs))).toBeLessThan(1);
28| // 루프 경계에서 불연속 클릭이 생기지 않도록 양 끝을 0으로 감싼다.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯
Test Files 1 failed (1)
Tests 3 failed (3)
Start at 04:57:18
Duration 902ms (environment 72%, setup 12%, tests 9%, transform 5%, worker 1%, import 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/audio.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 5 passed (5)
Start at 04:59:31
Duration 969ms (environment 71%, setup 11%, tests 11%, transform 5%, import 1%, worker 1%)
+179
View File
@@ -0,0 +1,179 @@
> find-word@0.1.0 test
> vitest run tests/audio.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/audio.test.ts (5 tests | 5 failed) 22ms
❯ 오디오 재생 수명과 믹서 (5)
× 생성 시 무음이며 gesture의 play에서만 장치를 연다 7ms
× 동시 시작을 거부하고 정지하면 모든 노드와 대기 Promise를 취소한다 8ms
× 로드 도중 취소한 이전 작업이 늦게 완료되어도 소리를 시작하지 않는다 3ms
× HTTP·디코드·장치 실패를 전달하고 다음 시도는 다시 로드한다 1ms
× 전체 음량은 두 신호의 비율을 보존하며 0부터 1 범위로 제한한다 1ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 5 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 생성 시 무음이며 gesture의 play에서만 장치를 연다
AssertionError: expected "vi.fn()" to be called once, but got 0 times
❯ tests/audio.test.ts:35:23
33| const started = vi.fn();
34| const done = f.engine.play(question, true, started);
35| expect(f.factory).toHaveBeenCalledOnce();
| ^
36| expect(f.context.resume).toHaveBeenCalledOnce();
37| await flush();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 동시 시작을 거부하고 정지하면 모든 노드와 대기 Promise를 취소한다
AssertionError: expected [Function] to throw error including '재생 중' but got '오디오 미구현'
Expected: "재생 중"
Received: "오디오 미구현"
❯ tests/audio.test.ts:54:58
52| const done = f.engine.play(question, false, vi.fn());
53| const cancelled = expect(done).rejects.toMatchObject({ name: 'Abor…
54| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
| ^
55| await flush();
56| f.engine.stop();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 동시 시작을 거부하고 정지하면 모든 노드와 대기 Promise를 취소한다
Error: Promise returned by `expect(actual).rejects.toMatchObject(expected)` was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not guaranteed to complete before the test finishes:
await expect(actual).rejects.toMatchObject(expected)
❯ tests/audio.test.ts:53:35
51| const f = fixture();
52| const done = f.engine.play(question, false, vi.fn());
53| const cancelled = expect(done).rejects.toMatchObject({ name: 'Abor…
| ^
54| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
55| await flush();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 로드 도중 취소한 이전 작업이 늦게 완료되어도 소리를 시작하지 않는다
AssertionError: expected Error: 오디오 미구현 to match object { name: 'AbortError' }
- Expected
+ Received
- {
- "name": "AbortError",
+ Error {
+ "message": "오디오 미구현",
}
❯ tests/audio.test.ts:68:35
66| f.fetcher.mockImplementationOnce(() => new Promise(resolve => { re…
67| const done = f.engine.play(question, false, vi.fn());
68| const cancelled = expect(done).rejects.toMatchObject({ name: 'Abor…
| ^
69| await flush();
70| f.engine.stop();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > HTTP·디코드·장치 실패를 전달하고 다음 시도는 다시 로드한다
AssertionError: expected [Function] to throw error including '음성 파일' but got '오디오 미구현'
Expected: "음성 파일"
Received: "오디오 미구현"
❯ tests/audio.test.ts:79:58
77| const f = fixture();
78| f.fetcher.mockResolvedValueOnce({ ok: false } as Response);
79| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
| ^
80| f.context.decodeAudioData.mockRejectedValueOnce(new Error('decode'…
81| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯
FAIL tests/audio.test.ts > 오디오 재생 수명과 믹서 > 전체 음량은 두 신호의 비율을 보존하며 0부터 1 범위로 제한한다
TypeError: Cannot read properties of undefined (reading 'gain')
❯ tests/audio.test.ts:94:24
92| const done = f.engine.play(question, false, vi.fn());
93| await flush();
94| expect(f.gains[0]!.gain.value).toBe(.3);
| ^
95| f.engine.setVolume(8);
96| expect(f.gains[0]!.gain.setTargetAtTime).toHaveBeenCalledWith(1, 2…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯
⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯
Vitest caught 3 unhandled errors during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
Error: 오디오 미구현
❯ AudioEngine.play src/audio/engine.ts:13:99
11| export class AudioEngine implements AudioPort {
12| constructor(_factory: () => AudioContext = () => new AudioContext(),…
13| async play(_question: Question, _intro: boolean, _onStarted: () => v…
| ^
14| stop(): void { /* RED 확인을 위한 인터페이스 자리만 확보한다. */ }
15| dispose(): void { /* 정리 동작은 취소 테스트 후 구현한다. */ }
❯ tests/audio.test.ts:34:27
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2794:26
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3334:20
❯ runWithCancel node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3329:10
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20
❯ runWithTimeout node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
AssertionError: expected Error: 오디오 미구현 to match object { name: 'AbortError' }
❯ Assertion.__VITEST_REJECTS__ node_modules/vitest/dist/chunks/index.uwTEEBsB.js:2480:33
❯ Assertion.propertyGetter node_modules/chai/index.js:1607:27
❯ Object.proxyGetter [as get] node_modules/chai/index.js:1696:22
❯ tests/audio.test.ts:53:35
51| const f = fixture();
52| const done = f.engine.play(question, false, vi.fn());
53| const cancelled = expect(done).rejects.toMatchObject({ name: 'Abor…
| ^
54| await expect(f.engine.play(question, false, vi.fn())).rejects.toTh…
55| await flush();
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2794:26
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3334:20
❯ runWithCancel node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3329:10
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
Error: 오디오 미구현
❯ AudioEngine.play src/audio/engine.ts:13:99
11| export class AudioEngine implements AudioPort {
12| constructor(_factory: () => AudioContext = () => new AudioContext(),…
13| async play(_question: Question, _intro: boolean, _onStarted: () => v…
| ^
14| stop(): void { /* RED 확인을 위한 인터페이스 자리만 확보한다. */ }
15| dispose(): void { /* 정리 동작은 취소 테스트 후 구현한다. */ }
❯ tests/audio.test.ts:92:27
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2794:26
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3334:20
❯ runWithCancel node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3329:10
❯ node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20
❯ runWithTimeout node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10
This error originated in "tests/audio.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Test Files 1 failed (1)
Tests 5 failed (5)
Errors 3 errors
Start at 04:58:29
Duration 885ms (environment 75%, setup 13%, transform 7%, tests 3%, import 1%, worker 1%)
+30
View File
@@ -0,0 +1,30 @@
> find-word@0.1.0 test
> vitest run tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/assets.test.ts (1 test | 1 failed) 7ms
❯ 저장한 한국어 음성 자산 (1)
× 모든 문장과 안내에 대응하는 로컬 WAV·출처·해시를 제공한다 5ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/assets.test.ts > 저장한 한국어 음성 자산 > 모든 문장과 안내에 대응하는 로컬 WAV·출처·해시를 제공한다
Error: ENOENT: no such file or directory, open 'public/audio/manifest.json'
❯ tests/assets.test.ts:11:33
9| describe('저장한 한국어 음성 자산', () => {
10| it('모든 문장과 안내에 대응하는 로컬 WAV·출처·해시를 제공한다', () => {
11| const manifest = JSON.parse(readFileSync('public/audio/manifest.js…
| ^
12| expect(manifest.generator).toContain('eSpeak NG');
13| const expected = [{ file: 'intro.wav', text: INTRO }, ...PATTERNS.…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (1)
Start at 04:59:59
Duration 280ms (setup 65%, transform 24%, import 5%, tests 4%, worker 3%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 2 passed (2)
Tests 7 passed (7)
Start at 05:07:53
Duration 3.03s (tests 71%, environment 18%, setup 7%, transform 3%, import 1%)
+638
View File
@@ -0,0 +1,638 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (6 tests | 1 failed) 1799ms
❯ 시작부터 결과까지 사용자 흐름 (6)
× 선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다 49ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다
TestingLibraryElementError: Found multiple elements with the role "radio" and name `/직접 입력/`
Here are the matching elements:
Ignored nodes: comments, script, style
<input
checked=""
name="mode"
type="radio"
value="mixed"
/>
Ignored nodes: comments, script, style
<input
name="mode"
type="radio"
value="text"
/>
(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).
Ignored nodes: comments, script, style
<body>
<div>
<a
class="skip-link"
href="#main"
>
본문으로 건너뛰기
</a>
<header
class="header wrap"
>
<div
class="brand"
>
<span
aria-hidden="true"
class="brand-mark"
>
<i />
<i />
<i />
<i />
</span>
<h1>
단어를 맞춰라
</h1>
<span
class="version"
>
v0.1
</span>
</div>
<div
class="header-actions"
>
<a
href="#howto"
>
플레이 방법 
<span
aria-hidden="true"
>
↗
</span>
</a>
<span
class="little-dot"
/>
소리에 집중하는 시간
</div>
</header>
<main
class="wrap"
id="main"
tabindex="-1"
>
<section
aria-labelledby="hero-title"
class="hero"
>
<div
class="hero-copy"
>
<p
class="eyebrow"
>
<span />
 귀를 기울이면, 발견하는 즐거움
</p>
<h2
id="hero-title"
>
소음 속 작은 단어,
<br />
<em>
들리시나요?
</em>
</h2>
<p
class="lead"
>
도시의 소리 사이에 에스가 숨겨 둔 단어.
<br />
조금 더 귀 기울여, 오늘의 행운을 찾아보세요.
</p>
<div
class="hero-meta"
>
<span>
10개의 질문
</span>
<span>
5단계 도전
</span>
<span>
나만의 속도로
</span>
</div>
</div>
<div
class="sound-room"
>
<div
class="room-top"
>
<span>
<i />
 에스의 사운드 룸
</span>
<span>
01 / 08
</span>
</div>
<div
class="orbit orbit-one"
/>
<div
class="orbit orbit-two"
/>
<span
aria-hidden="true"
class="floating-note note-one"
>
♪
</span>
<span
aria-hidden="true"
class="floating-note note-two"
>
✳
</span>
<div
class="speech-bubble"
>
행운의 단어를 찾아보거라!
</div>
<div
aria-hidden="true"
class="mascot "
>
<span
class="ear left"
/>
<span
class="ear right"
/>
<span
class="eye left"
/>
<span
class="eye right"
/>
<span
class="smile"
/>
<b>
S
</b>
</div>
<div
class="room-bottom"
>
<div
aria-hidden="true"
class="sound-wave "
>
<i
style="height: 10px; animation-delay: 0s;"
/>
<i
style="height: 33.799955450859244px; animation-delay: 0.05s;"
/>
<i
style="height: 16.132986448643948px; animation-delay: 0.1s;"
/>
<i
style="height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"
/>
<i
style="height: 21.858720427326595px; animation-delay: 0.2s;"
/>
<i
style="height: 29.163690702963766px; animation-delay: 0.25s;"
/>
<i
style="height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"
/>
<i
style="height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"
/>
<i
style="height: 30.619883556555898px; animation-delay: 0.4s;"
/>
<i
style="height: 35.39139668035928px; animation-delay: 0.45s;"
/>
<i
style="height: 71.52943948029164px; animation-delay: 0.5s;"
/>
<i
style="height: 19.53593765210872px; animation-delay: 0.55s;"
/>
<i
style="height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"
/>
<i
style="height: 16.952742107140878px; animation-delay: 0.65s;"
/>
<i
style="height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"
/>
<i
style="height: 32.979734657418774px; animation-delay: 0.75s;"
/>
<i
style="height: 66.27347594564635px; animation-delay: 0.8s;"
/>
<i
style="height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"
/>
<i
style="height: 56.615089141222136px; animation-delay: 0.9s;"
/>
<i
style="height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"
/>
<i
style="height: 43.861291911681526px; animation-delay: 1s;"
/>
<i
style="height: 68.21866699734562px; animation-delay: 1.05s;"
/>
<i
style="height: 28.858982496016484px; animation-delay: 1.1s;"
/>
<i
style="height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"
/>
<i
style="height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"
/>
<i
style="height: 73.74953619965402px; animation-delay: 1.25s;"
/>
<i
style="height: 23.8231850360459px; animation-delay: 1.3s;"
/>
<i
style="height: 32.57029511072814px; animation-delay: 1.35s;"
/>
<i
style="height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"
/>
<i
style="height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"
/>
<i
style="height: 26.085500220240995px; animation-delay: 1.5s;"
/>
<i
style="height: 25.59070868046883px; animation-delay: 1.55s;"
/>
<i
style="height: 30.10305417152866px; animation-delay: 1.6s;"
/>
<i
style="height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"
/>
<i
style="height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"
/>
</div>
<span>
소음 사이, 숨겨진 한마디
</span>
</div>
</div>
</section>
<section
aria-labelledby="setup-title"
class="setup-card"
>
<div
class="setup-heading"
>
<span
class="section-number"
>
01
</span>
<div>
<h2
id="setup-title"
>
나의 소리 탐험 준비
</h2>
<p>
편한 방식으로 시작하세요. 난이도는 조금씩 높아져요.
</p>
</div>
<span
class="round-badge"
>
약 3–5분
</span>
</div>
<div
class="setup-grid"
>
<fieldset
class="mode-field"
>
<legend>
어떻게 답할까요?
</legend>
<div
class="mode-options"
>
<label
class="mode-option selected"
>
<input
checked=""
name="mode"
type="radio"
value="mixed"
/>
<span
aria-hidden="true"
class="mode-icon"
>
◈
</span>
<strong>
골고루 풀기
</strong>
<small>
선택 + 직접 입력
</small>
</label>
<label
class="mode-option "
>
<input
name="mode"
type="radio"
value="choice"
/>
<span
aria-hidden="true"
class="mode-icon"
>
☷
</span>
<strong>
보기에서 선택
</strong>
<small>
4개 단어 중 하나
</small>
</label>
<label
class="mode-option "
>
<input
name="mode"
type="radio"
value="text"
/>
<span
aria-hidden="true"
class="mode-icon"
>
✎
</span>
<strong>
직접 입력
</strong>
<small>
기억한 단어 쓰기
</small>
</label>
</div>
</fieldset>
<div
class="environment-field"
>
<label
for="environment"
>
배경 환경
</label>
<select
id="environment"
>
<option
value="rotate"
>
여덟 가지 일상 속으로
</option>
<option
value="street"
>
길거리
</option>
<option
value="mall"
>
쇼핑몰
</option>
<option
value="theater"
>
극장
</option>
<option
value="department"
>
백화점
</option>
<option
value="market"
>
시장
</option>
<option
value="subway"
>
지하철
</option>
<option
value="cafe"
>
카페
</option>
<option
value="rain"
>
비 오는 거리
</option>
</select>
<p>
길거리부터 비 오는 거리까지, 매번 다른 공간을 만나요.
</p>
<div
class="environment-chips"
>
<span>
길거리
</span>
<span>
카페
</span>
<span>
지하철
</span>
<span>
+5개 환경
</span>
</div>
</div>
</div>
<div
class="start-row"
>
<p>
<span
aria-hidden="true"
>
♬
</span>
 편안한 작은 음량으로 시작해 보세요.
</p>
<button
class="primary"
>
소리 시작 
<span
aria-hidden="true"
>
→
</span>
</button>
</div>
</section>
<section
aria-labelledby="howto-title"
class="howto"
id="howto"
>
<h2
id="howto-title"
>
작은 단어를 발견하는 방법
</h2>
<div
class="steps"
>
<article>
<span>
01
</span>
<h3>
먼저, 귀 기울이기
</h3>
<p>
에스의 안내와 소음 속 문장을 들어요.
<br />
처음에는 또렷하게, 점점 더 작게.
</p>
</article>
<article>
<span>
02
</span>
<h3>
기억한 단어 찾기
</h3>
<p>
소리가 끝나면 질문이 나타나요.
<br />
보기에서 고르거나 직접 입력해요.
</p>
</article>
<article>
<span>
03
</span>
<h3>
나의 발견 돌아보기
</h3>
<p>
열 문제 뒤 나의 결과를 확인해요.
<br />
놓쳤다면 언제든 다시 들어도 좋아요.
</p>
</article>
</div>
</section>
<div
class="volume-row"
>
<label
for="volume"
>
<span
aria-hidden="true"
>
◖))
</span>
 전체 음량
</label>
<input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
<output
for="volume"
>
35
%
</output>
</div>
</main>
<footer
class="footer wrap"
>
<p>
Designed by OZ at HearT-M Medical Inc.
</p>
<p>
청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.
</p>
<span>
개인정보 수집 없이, 오직 플레이만.
</span>
</footer>
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ getElementError node_modules/@testing-library/dom/dist/query-helpers.js:20:35
❯ getMultipleElementsFoundError node_modules/@testing-library/dom/dist/query-helpers.js:23:10
❯ node_modules/@testing-library/dom/dist/query-helpers.js:55:13
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:87:31
85| it('선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다', async () => {
86| const f = fixture();
87| await f.user.click(screen.getByRole('radio', { name: /직접 입력/ }));
| ^
88| await f.user.selectOptions(screen.getByLabelText('배경 환경'), 'rain');
89| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed | 1 passed (2)
Tests 1 failed | 6 passed (7)
Start at 05:07:31
Duration 2.86s (tests 69%, environment 19%, setup 6%, transform 3%, import 2%)
+215
View File
@@ -0,0 +1,215 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (6 tests | 6 failed) 192ms
❯ 시작부터 결과까지 사용자 흐름 (6)
× 브랜드·버전·크레딧을 보여주며 직접 시작하기 전에는 재생하지 않는다 147ms
× 재생 동안 답을 숨기고 중복 클릭을 막으며 끝나면 네 선택지에 접근할 수 있다 11ms
× 혼합 모드 열 문제를 키보드·텍스트로 풀고 결과에서 다시 시작한다 9ms
× 오디오 오류를 알리고 재시도하며 나가면 장치를 정리한다 10ms
× 선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다 7ms
× 음량 조절이 엔진에 전달되고 세션 종료 뒤 늦은 완료는 무시한다 6ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 6 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 브랜드·버전·크레딧을 보여주며 직접 시작하기 전에는 재생하지 않는다
TestingLibraryElementError: Unable to find an accessible element with the role "heading" and name "단어를 맞춰라"
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:25:19
23| it('브랜드·버전·크레딧을 보여주며 직접 시작하기 전에는 재생하지 않는다', () => {
24| const f = fixture();
25| expect(screen.getByRole('heading', { name: '단어를 맞춰라' })).toBeInThe…
| ^
26| expect(screen.getByText('Designed by OZ at HearT-M Medical Inc.'))…
27| expect(screen.getByText('v0.1')).toBeInTheDocument();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 재생 동안 답을 숨기고 중복 클릭을 막으며 끝나면 네 선택지에 접근할 수 있다
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/소리 시작/`
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:33:31
31| it('재생 동안 답을 숨기고 중복 클릭을 막으며 끝나면 네 선택지에 접근할 수 있다', async () => {
32| const f = fixture();
33| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
| ^
34| expect(f.audio.play).toHaveBeenCalledTimes(1);
35| expect(screen.queryByRole('group', { name: '답 선택' })).not.toBeInTh…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 혼합 모드 열 문제를 키보드·텍스트로 풀고 결과에서 다시 시작한다
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/소리 시작/`
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:51:31
49| const f = fixture();
50| const questions = createQuestions(42, 'mixed');
51| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
| ^
52| for (let i = 0; i < questions.length; i++) {
53| if (i) await f.user.click(screen.getByRole('button', { name: /문장…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 오디오 오류를 알리고 재시도하며 나가면 장치를 정리한다
TestingLibraryElementError: Unable to find an accessible element with the role "button" and name `/소리 시작/`
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:76:31
74| it('오디오 오류를 알리고 재시도하며 나가면 장치를 정리한다', async () => {
75| const f = fixture();
76| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
| ^
77| await f.fail();
78| expect(screen.getByRole('alert')).toHaveTextContent(/소리를 재생하지 못했어요…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다
TestingLibraryElementError: Unable to find an accessible element with the role "radio" and name `/직접 입력/`
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:87:31
85| it('선택한 주관식·배경 환경으로 시작하고 빈 답변을 제출하지 않는다', async () => {
86| const f = fixture();
87| await f.user.click(screen.getByRole('radio', { name: /직접 입력/ }));
| ^
88| await f.user.selectOptions(screen.getByLabelText('배경 환경'), 'rain');
89| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 음량 조절이 엔진에 전달되고 세션 종료 뒤 늦은 완료는 무시한다
TestingLibraryElementError: Unable to find an accessible element with the role "slider" and name "전체 음량"
Here are the accessible roles:
main:
Name "":
<main />
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<main />
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:100:29
98| it('음량 조절이 엔진에 전달되고 세션 종료 뒤 늦은 완료는 무시한다', async () => {
99| const f = fixture();
100| fireEvent.change(screen.getByRole('slider', { name: '전체 음량' }), { …
| ^
101| expect(f.audio.setVolume).toHaveBeenCalledWith(.2);
102| await f.user.click(screen.getByRole('button', { name: /소리 시작/ }));
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯
Test Files 1 failed (1)
Tests 6 failed (6)
Start at 05:01:34
Duration 1.08s (environment 60%, tests 20%, setup 10%, transform 5%, import 4%, worker 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/build.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 05:09:30
Duration 438ms (tests 57%, setup 32%, transform 9%, worker 1%, import 1%)
+34
View File
@@ -0,0 +1,34 @@
> find-word@0.1.0 test
> vitest run tests/build.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/build.test.ts (1 test | 1 failed) 61ms
❯ production 자산 검증기 (1)
× 101개 음성 포함을 확인하고 누락·변조는 실패시킨다 60ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/build.test.ts > production 자산 검증기 > 101개 음성 포함을 확인하고 누락·변조는 실패시킨다
AssertionError: expected '0' to be '101' // Object.is equality
Expected: "101"
Received: "0"
❯ tests/build.test.ts:16:31
14| try {
15| cpSync('public/audio', join(temporary, 'audio'), { recursive: tr…
16| expect(verify().trim()).toBe('101');
| ^
17| writeFileSync(join(temporary, 'audio/intro.wav'), 'broken');
18| expect(verify).toThrow();
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed (1)
Start at 05:08:45
Duration 318ms (setup 52%, tests 31%, transform 14%, worker 2%, import 2%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 4 passed (4)
Start at 05:11:10
Duration 878ms (environment 78%, setup 14%, transform 5%, tests 1%, worker 1%)
+32
View File
@@ -0,0 +1,32 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/content.test.ts (4 tests | 1 failed) 13ms
❯ 저장된 문제·환경 데이터 (4)
× 오답마다 서로 다른 변형 혼동 난이도를 추적할 수 있다 6ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/content.test.ts > 저장된 문제·환경 데이터 > 오답마다 서로 다른 변형 혼동 난이도를 추적할 수 있다
TypeError: actual value must be number or bigint, received "undefined"
❯ tests/content.test.ts:10:42
8| WORDS.forEach(entry => {
9| const ranks = entry.distractors.map(d => RULE_DIFFICULTY[d.rule]…
10| ranks.forEach(rank => expect(rank).toBeGreaterThan(0));
| ^
11| expect(new Set(ranks).size).toBe(3);
12| });
❯ tests/content.test.ts:10:13
❯ tests/content.test.ts:8:11
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 3 passed (4)
Start at 05:10:58
Duration 885ms (environment 78%, setup 13%, transform 6%, tests 2%, worker 1%, import 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 7 passed (7)
Start at 05:16:05
Duration 2.96s (tests 71%, environment 20%, setup 4%, transform 4%, import 1%)
+36
View File
@@ -0,0 +1,36 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (7 tests | 1 failed) 2021ms
❯ 시작부터 결과까지 사용자 흐름 (7)
× 한글 조합을 확정하는 Enter는 채점하지 않고 다음 제출을 기다린다 345ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 한글 조합을 확정하는 Enter는 채점하지 않고 다음 제출을 기다린다
Error: expect(element).not.toBeInTheDocument()
expected document not to contain element, found <h2
tabindex="-1"
>
정답이에요!
</h2> instead
❯ tests/App.test.tsx:56:46
54| fireEvent.change(textbox, { target: { value: createQuestions(42, "…
55| fireEvent.submit(textbox.closest("form")!);
56| expect(screen.queryByText("정답이에요!")).not.toBeInTheDocument();
| ^
57| fireEvent.compositionEnd(textbox);
58| fireEvent.submit(textbox.closest("form")!);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 6 passed (7)
Start at 05:15:12
Duration 2.96s (tests 71%, environment 20%, transform 4%, setup 3%, import 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 한글 조합 도중
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed | 7 skipped (8)
Start at 05:17:57
Duration 1.38s (environment 44%, tests 38%, setup 8%, transform 7%, import 3%, worker 1%)
+395
View File
@@ -0,0 +1,395 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 한글 조합 도중
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (8 tests | 1 failed | 7 skipped) 488ms
❯ 시작부터 결과까지 사용자 흐름 (8)
× 한글 조합 도중 처음으로 돌아가도 새 세션의 입력은 정상 제출된다 487ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 한글 조합 도중 처음으로 돌아가도 새 세션의 입력은 정상 제출된다
TestingLibraryElementError: Unable to find an element with the text: 정답이에요!. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<a
class="skip-link"
href="#main"
>
본문으로 건너뛰기
</a>
<header
class="header wrap"
>
<div
class="brand"
>
<span
aria-hidden="true"
class="brand-mark"
>
<i />
<i />
<i />
<i />
</span>
<h1>
단어를 맞춰라
</h1>
<span
class="version"
>
v0.1
</span>
</div>
<div
class="header-actions"
>
<button
class="text-button"
>
처음으로
</button>
<span
class="little-dot"
/>
소리에 집중하는 시간
</div>
</header>
<main
class="wrap"
id="main"
tabindex="-1"
>
<section
aria-label="단어 찾기 게임"
class="game-layout"
>
<div
class="game-top"
>
<span
class="eyebrow"
>
나의 소리 탐험
</span>
<span>
문제 
<b>
01
</b>
 / 10
</span>
</div>
<progress
aria-label="전체 진행률"
max="10"
value="0"
/>
<div
class="game-columns"
>
<aside
class="environment-panel"
>
<p
class="eyebrow"
>
지금 듣고 있는 공간
</p>
<h2>
길거리
</h2>
<p>
지나가는 차와 도시의 웅성임
</p>
<div
aria-hidden="true"
class="mascot small"
>
<span
class="ear left"
/>
<span
class="ear right"
/>
<span
class="eye left"
/>
<span
class="eye right"
/>
<span
class="smile"
/>
<b>
S
</b>
</div>
<div
aria-hidden="true"
class="sound-wave "
>
<i
style="height: 10px; animation-delay: 0s;"
/>
<i
style="height: 33.799955450859244px; animation-delay: 0.05s;"
/>
<i
style="height: 16.132986448643948px; animation-delay: 0.1s;"
/>
<i
style="height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"
/>
<i
style="height: 21.858720427326595px; animation-delay: 0.2s;"
/>
<i
style="height: 29.163690702963766px; animation-delay: 0.25s;"
/>
<i
style="height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"
/>
<i
style="height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"
/>
<i
style="height: 30.619883556555898px; animation-delay: 0.4s;"
/>
<i
style="height: 35.39139668035928px; animation-delay: 0.45s;"
/>
<i
style="height: 71.52943948029164px; animation-delay: 0.5s;"
/>
<i
style="height: 19.53593765210872px; animation-delay: 0.55s;"
/>
<i
style="height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"
/>
<i
style="height: 16.952742107140878px; animation-delay: 0.65s;"
/>
<i
style="height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"
/>
<i
style="height: 32.979734657418774px; animation-delay: 0.75s;"
/>
<i
style="height: 66.27347594564635px; animation-delay: 0.8s;"
/>
<i
style="height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"
/>
<i
style="height: 56.615089141222136px; animation-delay: 0.9s;"
/>
<i
style="height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"
/>
<i
style="height: 43.861291911681526px; animation-delay: 1s;"
/>
<i
style="height: 68.21866699734562px; animation-delay: 1.05s;"
/>
<i
style="height: 28.858982496016484px; animation-delay: 1.1s;"
/>
<i
style="height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"
/>
<i
style="height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"
/>
<i
style="height: 73.74953619965402px; animation-delay: 1.25s;"
/>
<i
style="height: 23.8231850360459px; animation-delay: 1.3s;"
/>
<i
style="height: 32.57029511072814px; animation-delay: 1.35s;"
/>
<i
style="height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"
/>
<i
style="height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"
/>
<i
style="height: 26.085500220240995px; animation-delay: 1.5s;"
/>
<i
style="height: 25.59070868046883px; animation-delay: 1.55s;"
/>
<i
style="height: 30.10305417152866px; animation-delay: 1.6s;"
/>
<i
style="height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"
/>
<i
style="height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"
/>
</div>
<div
class="level-tag"
>
1
단계 · 
+
6
 dB
</div>
<p
class="db-note"
>
음성 / 배경의 상대 음량
<br />
소리에 익숙해지기
</p>
<div
class="mini-stats"
>
<span>
점수 
<b>
0
</b>
</span>
<span>
연속 정답 
<b>
0
</b>
</span>
</div>
</aside>
<div
class="question-panel panel"
>
<div
class="question-meta"
>
<span>
마지막 단어
</span>
<span>
주관식
</span>
</div>
<h2
tabindex="-1"
>
마지막에 나온 단어는 무엇인가요?
</h2>
<p
class="muted"
>
기억나는 단어 하나를 입력해 주세요.
</p>
<form>
<label
for="answer"
>
들은 단어
</label>
<input
autocomplete="off"
id="answer"
maxlength="60"
placeholder="여기에 단어를 적어 주세요"
value="기름"
/>
<button
class="primary"
>
정답 확인
</button>
</form>
<div
class="play-controls"
>
<button
class="secondary"
>
↻ 다시 듣기
</button>
<small>
다시 들어도 점수는 줄어들지 않아요.
</small>
</div>
</div>
</div>
</section>
<div
class="volume-row"
>
<label
for="volume"
>
<span
aria-hidden="true"
>
◖))
</span>
 전체 음량
</label>
<input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
<output
for="volume"
>
35
%
</output>
</div>
</main>
<footer
class="footer wrap"
>
<p>
Designed by OZ at HearT-M Medical Inc.
</p>
<p>
청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.
</p>
<span>
개인정보 수집 없이, 오직 플레이만.
</span>
</footer>
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:58:19
56| await f.user.type(screen.getByRole("textbox", { name: "들은 단어" }), …
57| await f.user.click(screen.getByRole("button", { name: "정답 확인" }));
58| expect(screen.getByText("정답이에요!")).toBeInTheDocument();
| ^
59| });
60| it("한글 조합을 확정하는 Enter는 채점하지 않고 다음 제출을 기다린다", async () => {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 7 skipped (8)
Start at 05:17:35
Duration 1.42s (environment 43%, tests 38%, transform 8%, setup 7%, import 3%, worker 1%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 2 passed (2)
Start at 05:18:31
Duration 736ms (tests 79%, setup 17%, transform 2%, import 1%, worker 1%)
+30
View File
@@ -0,0 +1,30 @@
> find-word@0.1.0 test
> vitest run tests/assets.test.ts -t 도구 버전
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/assets.test.ts (2 tests | 1 failed | 1 skipped) 8ms
❯ 저장한 한국어 음성 자산 (2)
× 실제 생성 도구 버전을 기록하여 재생성의 출처를 추적한다 6ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/assets.test.ts > 저장한 한국어 음성 자산 > 실제 생성 도구 버전을 기록하여 재생성의 출처를 추적한다
TypeError: .toMatch() expects to receive a string, but got undefined
❯ tests/assets.test.ts:12:43
10| it("실제 생성 도구 버전을 기록하여 재생성의 출처를 추적한다", () => {
11| const manifest = JSON.parse(readFileSync("public/audio/manifest.js…
12| expect(manifest.toolVersions?.espeak).toMatch(/eSpeak NG.*\d+\.\d+…
| ^
13| expect(manifest.toolVersions?.ffmpeg).toMatch(/ffmpeg version \d+\…
14| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 1 skipped (2)
Start at 05:17:59
Duration 285ms (setup 63%, transform 25%, import 5%, tests 5%, worker 2%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 한글 조합 도중
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed | 7 skipped (8)
Start at 05:19:29
Duration 1.45s (environment 43%, tests 39%, setup 8%, transform 6%, import 3%, worker 1%)
+395
View File
@@ -0,0 +1,395 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 한글 조합 도중
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (8 tests | 1 failed | 7 skipped) 542ms
❯ 시작부터 결과까지 사용자 흐름 (8)
× 한글 조합 도중 처음으로 돌아가도 새 세션의 입력은 정상 제출된다 539ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 한글 조합 도중 처음으로 돌아가도 새 세션의 입력은 정상 제출된다
TestingLibraryElementError: Unable to find an element with the text: 정답이에요!. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<a
class="skip-link"
href="#main"
>
본문으로 건너뛰기
</a>
<header
class="header wrap"
>
<div
class="brand"
>
<span
aria-hidden="true"
class="brand-mark"
>
<i />
<i />
<i />
<i />
</span>
<h1>
단어를 맞춰라
</h1>
<span
class="version"
>
v0.1
</span>
</div>
<div
class="header-actions"
>
<button
class="text-button"
>
처음으로
</button>
<span
class="little-dot"
/>
소리에 집중하는 시간
</div>
</header>
<main
class="wrap"
id="main"
tabindex="-1"
>
<section
aria-label="단어 찾기 게임"
class="game-layout"
>
<div
class="game-top"
>
<span
class="eyebrow"
>
나의 소리 탐험
</span>
<span>
문제 
<b>
01
</b>
 / 10
</span>
</div>
<progress
aria-label="전체 진행률"
max="10"
value="0"
/>
<div
class="game-columns"
>
<aside
class="environment-panel"
>
<p
class="eyebrow"
>
지금 듣고 있는 공간
</p>
<h2>
길거리
</h2>
<p>
지나가는 차와 도시의 웅성임
</p>
<div
aria-hidden="true"
class="mascot small"
>
<span
class="ear left"
/>
<span
class="ear right"
/>
<span
class="eye left"
/>
<span
class="eye right"
/>
<span
class="smile"
/>
<b>
S
</b>
</div>
<div
aria-hidden="true"
class="sound-wave "
>
<i
style="height: 10px; animation-delay: 0s;"
/>
<i
style="height: 33.799955450859244px; animation-delay: 0.05s;"
/>
<i
style="height: 16.132986448643948px; animation-delay: 0.1s;"
/>
<i
style="height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"
/>
<i
style="height: 21.858720427326595px; animation-delay: 0.2s;"
/>
<i
style="height: 29.163690702963766px; animation-delay: 0.25s;"
/>
<i
style="height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"
/>
<i
style="height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"
/>
<i
style="height: 30.619883556555898px; animation-delay: 0.4s;"
/>
<i
style="height: 35.39139668035928px; animation-delay: 0.45s;"
/>
<i
style="height: 71.52943948029164px; animation-delay: 0.5s;"
/>
<i
style="height: 19.53593765210872px; animation-delay: 0.55s;"
/>
<i
style="height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"
/>
<i
style="height: 16.952742107140878px; animation-delay: 0.65s;"
/>
<i
style="height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"
/>
<i
style="height: 32.979734657418774px; animation-delay: 0.75s;"
/>
<i
style="height: 66.27347594564635px; animation-delay: 0.8s;"
/>
<i
style="height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"
/>
<i
style="height: 56.615089141222136px; animation-delay: 0.9s;"
/>
<i
style="height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"
/>
<i
style="height: 43.861291911681526px; animation-delay: 1s;"
/>
<i
style="height: 68.21866699734562px; animation-delay: 1.05s;"
/>
<i
style="height: 28.858982496016484px; animation-delay: 1.1s;"
/>
<i
style="height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"
/>
<i
style="height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"
/>
<i
style="height: 73.74953619965402px; animation-delay: 1.25s;"
/>
<i
style="height: 23.8231850360459px; animation-delay: 1.3s;"
/>
<i
style="height: 32.57029511072814px; animation-delay: 1.35s;"
/>
<i
style="height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"
/>
<i
style="height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"
/>
<i
style="height: 26.085500220240995px; animation-delay: 1.5s;"
/>
<i
style="height: 25.59070868046883px; animation-delay: 1.55s;"
/>
<i
style="height: 30.10305417152866px; animation-delay: 1.6s;"
/>
<i
style="height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"
/>
<i
style="height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"
/>
</div>
<div
class="level-tag"
>
1
단계 · 
+
6
 dB
</div>
<p
class="db-note"
>
음성 / 배경의 상대 음량
<br />
소리에 익숙해지기
</p>
<div
class="mini-stats"
>
<span>
점수 
<b>
0
</b>
</span>
<span>
연속 정답 
<b>
0
</b>
</span>
</div>
</aside>
<div
class="question-panel panel"
>
<div
class="question-meta"
>
<span>
마지막 단어
</span>
<span>
주관식
</span>
</div>
<h2
tabindex="-1"
>
마지막에 나온 단어는 무엇인가요?
</h2>
<p
class="muted"
>
기억나는 단어 하나를 입력해 주세요.
</p>
<form>
<label
for="answer"
>
들은 단어
</label>
<input
autocomplete="off"
id="answer"
maxlength="60"
placeholder="여기에 단어를 적어 주세요"
value="기름"
/>
<button
class="primary"
>
정답 확인
</button>
</form>
<div
class="play-controls"
>
<button
class="secondary"
>
↻ 다시 듣기
</button>
<small>
다시 들어도 점수는 줄어들지 않아요.
</small>
</div>
</div>
</div>
</section>
<div
class="volume-row"
>
<label
for="volume"
>
<span
aria-hidden="true"
>
◖))
</span>
 전체 음량
</label>
<input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
<output
for="volume"
>
35
%
</output>
</div>
</main>
<footer
class="footer wrap"
>
<p>
Designed by OZ at HearT-M Medical Inc.
</p>
<p>
청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.
</p>
<span>
개인정보 수집 없이, 오직 플레이만.
</span>
</footer>
</div>
</body>
❯ Object.getElementError node_modules/@testing-library/dom/dist/config.js:37:19
❯ node_modules/@testing-library/dom/dist/query-helpers.js:76:38
❯ node_modules/@testing-library/dom/dist/query-helpers.js:52:17
❯ node_modules/@testing-library/dom/dist/query-helpers.js:95:19
❯ tests/App.test.tsx:61:19
59| await f.user.type(screen.getByRole("textbox", { name: "들은 단어" }), …
60| await f.user.click(screen.getByRole("button", { name: "정답 확인" }));
61| expect(screen.getByText("정답이에요!")).toBeInTheDocument();
| ^
62| });
63| it("한글 조합을 확정하는 Enter는 채점하지 않고 다음 제출을 기다린다", async () => {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 7 skipped (8)
Start at 05:18:58
Duration 1.41s (environment 43%, tests 42%, setup 7%, transform 4%, import 3%, worker 1%)
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-Bq8F62nv.css 14.90 kB │ gzip: 4.30 kB
dist/assets/index-DKxtGloM.js 244.26 kB │ gzip: 76.95 kB
✓ built in 157ms
빌드 음성 자산 101개 확인
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-BEQhGx9x.css 14.87 kB │ gzip: 4.29 kB
dist/assets/index-ZqUFJyNr.js 244.26 kB │ gzip: 76.95 kB
✓ built in 162ms
빌드 음성 자산 101개 확인
+14
View File
@@ -0,0 +1,14 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --project=mobile
[WebServer] (node:752303) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 1 test using 1 worker
(node:752323) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [mobile] › tests/browser/ui.spec.ts:6:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (13.6s)
1 passed (14.7s)
+112
View File
@@ -0,0 +1,112 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --project=mobile
[WebServer] (node:752009) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 1 test using 1 worker
(node:752029) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [mobile] › tests/browser/ui.spec.ts:6:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (13.7s)
1) [mobile] › tests/browser/ui.spec.ts:6:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 ───────────────────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 7
+ Received + 7
Array [
Object {
"title": "단어를 맞춰라",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "소음 속 작은 단어,들리시나요?",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "나의 소리 탐험 준비",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "작은 단어를 발견하는 방법",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "먼저, 귀 기울이기",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "기억한 단어 찾기",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "나의 발견 돌아보기",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
]
17 | );
18 | const homeHeadings = await wordBreaks();
> 19 | expect.soft(homeHeadings).toEqual(
| ^
20 | homeHeadings.map((heading) => ({ ...heading, wordBreak: "keep-all" })),
21 | );
22 | await page.getByRole("button", { name: /소리 시작/ }).click();
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:19:29
Error: expect(received).toEqual(expected) // deep equality
- Expected - 3
+ Received + 3
Array [
Object {
"title": "단어를 맞춰라",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "길거리",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
Object {
"title": "말을 바로잡아 알려준 단어는 무엇인가요?",
- "wordBreak": "keep-all",
+ "wordBreak": "normal",
},
]
30 | });
31 | const gameHeadings = await wordBreaks();
> 32 | expect.soft(gameHeadings).toEqual(
| ^
33 | gameHeadings.map((heading) => ({ ...heading, wordBreak: "keep-all" })),
34 | );
35 | expect(
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:32:29
Error Context: test-results/ui-한국어-제목은-단어-중간에서-줄바꿈하지-않는다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-한국어-제목은-단어-중간에서-줄바꿈하지-않는다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/ui-한국어-제목은-단어-중간에서-줄바꿈하지-않는다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
1 failed
[mobile] › tests/browser/ui.spec.ts:6:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 ────────────────────────────
+14
View File
@@ -0,0 +1,14 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --project=mobile --grep 난이도 설명
[WebServer] (node:752930) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 1 test using 1 worker
(node:752950) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [mobile] › tests/browser/ui.spec.ts:4:1 › 난이도 설명은 모바일에서도 보인다 (803ms)
1 passed (2.0s)
+95
View File
@@ -0,0 +1,95 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --project=mobile --grep 난이도 설명
[WebServer] (node:752587) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 1 test using 1 worker
(node:752608) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [mobile] › tests/browser/ui.spec.ts:4:1 › 난이도 설명은 모바일에서도 보인다 (4.0s)
1) [mobile] › tests/browser/ui.spec.ts:4:1 › 난이도 설명은 모바일에서도 보인다 ──────────────────────────────────
Error: expect(locator).toBeVisible() failed
Locator: locator('.db-note')
Expected: visible
Received: hidden
Timeout: 1000ms
Call log:
- Expect "soft toBeVisible" locator('.db-note') with timeout 1000ms
- waiting for locator('.db-note')
10 × locator resolved to <p class="db-note">…</p>
- unexpected value "hidden"
7 | // DOM에만 설명을 남기고 모바일 CSS로 숨기면 컴포넌트 검증은 통과하므로
8 | // 실제 화면에서 난이도 배지와 쉬운 설명을 모두 읽을 수 있는지 확인한다.
> 9 | await expect.soft(page.locator(".db-note")).toBeVisible({ timeout: 1000 });
| ^
10 | await expect.soft(page.getByText("1단계 · SNR +6 dB")).toBeVisible({ timeout: 1000 });
11 | await expect.soft(page.locator(".db-note")).toContainText("음성이 배경보다 6 dB 커요.", { timeout: 1000 });
12 | });
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:9:47
Error: expect(locator).toBeVisible() failed
Locator: getByText('1단계 · SNR +6 dB')
Expected: visible
Timeout: 1000ms
Error: element(s) not found
Call log:
- Expect "soft toBeVisible" getByText('1단계 · SNR +6 dB') with timeout 1000ms
- waiting for getByText('1단계 · SNR +6 dB')
8 | // 실제 화면에서 난이도 배지와 쉬운 설명을 모두 읽을 수 있는지 확인한다.
9 | await expect.soft(page.locator(".db-note")).toBeVisible({ timeout: 1000 });
> 10 | await expect.soft(page.getByText("1단계 · SNR +6 dB")).toBeVisible({ timeout: 1000 });
| ^
11 | await expect.soft(page.locator(".db-note")).toContainText("음성이 배경보다 6 dB 커요.", { timeout: 1000 });
12 | });
13 |
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:10:56
Error: expect(locator).toContainText(expected) failed
Locator: locator('.db-note')
Expected substring: "음성이 배경보다 6 dB 커요."
Received string: "음성 / 배경의 상대 음량소리에 익숙해지기"
Timeout: 1000ms
Call log:
- Expect "soft toContainText" locator('.db-note') with timeout 1000ms
- waiting for locator('.db-note')
11 × locator resolved to <p class="db-note">…</p>
- unexpected value "음성 / 배경의 상대 음량소리에 익숙해지기"
9 | await expect.soft(page.locator(".db-note")).toBeVisible({ timeout: 1000 });
10 | await expect.soft(page.getByText("1단계 · SNR +6 dB")).toBeVisible({ timeout: 1000 });
> 11 | await expect.soft(page.locator(".db-note")).toContainText("음성이 배경보다 6 dB 커요.", { timeout: 1000 });
| ^
12 | });
13 |
14 | // jsdom은 줄바꿈을 계산하지 않는다. 실제 Chrome에서 한국어 제목의
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:11:47
Error Context: test-results/ui-난이도-설명은-모바일에서도-보인다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-난이도-설명은-모바일에서도-보인다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/ui-난이도-설명은-모바일에서도-보인다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
1 failed
[mobile] › tests/browser/ui.spec.ts:4:1 › 난이도 설명은 모바일에서도 보인다 ───────────────────────────────────
+15
View File
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-Cqbkt0Zp.css 14.92 kB │ gzip: 4.30 kB
dist/assets/index-BlNwW4oG.js 244.36 kB │ gzip: 76.99 kB
✓ built in 169ms
빌드 음성 자산 101개 확인
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 다섯 난이도의 SNR
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed | 8 skipped (9)
Start at 05:31:02
Duration 3.24s (tests 61%, environment 27%, transform 5%, setup 5%, import 2%, worker 1%)
+104
View File
@@ -0,0 +1,104 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 다섯 난이도의 SNR
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (9 tests | 1 failed | 8 skipped) 1328ms
❯ 시작부터 결과까지 사용자 흐름 (9)
× 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다 1327ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
Error: expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Received has type: Null
Received has value: null
❯ tests/App.test.tsx:65:73
63| await f.finish();
64| const [label, description] = levels[Math.floor(index / 2)]!;
65| expect.soft(screen.queryByText(`${question.level}단계 · ${label}`)…
| ^
66| expect.soft(screen.queryByText(description, { exact: false })).t…
67| await f.user.click(screen.getByRole("button", { name: new RegExp…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/26]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
Error: expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Received has type: Null
Received has value: null
❯ tests/App.test.tsx:66:70
64| const [label, description] = levels[Math.floor(index / 2)]!;
65| expect.soft(screen.queryByText(`${question.level}단계 · ${label}`)…
66| expect.soft(screen.queryByText(description, { exact: false })).t…
| ^
67| await f.user.click(screen.getByRole("button", { name: new RegExp…
68| await f.user.click(screen.getByRole("button", { name: index === …
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/26]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
Error: expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Received has type: Null
Received has value: null
❯ tests/App.test.tsx:71:69
69| }
70| const table = within(screen.getByRole("table", { name: "난이도별 결과" }…
71| expect.soft(table.queryByRole("columnheader", { name: "SNR" })).to…
| ^
72| for (const [label] of levels) {
73| expect.soft(table.queryByRole("cell", { name: label })).toBeInTh…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/26]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 다섯 난이도의 SNR과 음성·배경 크기 관계를 설명하고 결과에도 표시한다
Error: expect(received).toBeInTheDocument()
received value must be an HTMLElement or an SVGElement.
Received has type: Null
Received has value: null
❯ tests/App.test.tsx:73:63
71| expect.soft(table.queryByRole("columnheader", { name: "SNR" })).to…
72| for (const [label] of levels) {
73| expect.soft(table.queryByRole("cell", { name: label })).toBeInTh…
| ^
74| }
75| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/26]⎯
Test Files 1 failed (1)
Tests 1 failed | 8 skipped (9)
Start at 05:30:12
Duration 2.39s (tests 63%, environment 27%, setup 5%, transform 2%, import 2%)
@@ -0,0 +1,17 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --grep 전체 음량
[WebServer] (node:753690) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 2 tests using 2 workers
(node:753711) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:753710) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.6s)
✓ 2 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.7s)
2 passed (3.0s)
+147
View File
@@ -0,0 +1,147 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --grep 전체 음량
[WebServer] (node:753191) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 2 tests using 2 workers
(node:753211) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:753212) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 2 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.8s)
✘ 1 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.8s)
1) [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ─────────────────────
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 919.5625
Received: 1203.578125
12 | expect.soft(control.y).toBeGreaterThanOrEqual(bounds.y);
13 | expect.soft(control.x + control.width).toBeLessThanOrEqual(bounds.x + bounds.width);
> 14 | expect.soft(control.y + control.height).toBeLessThanOrEqual(bounds.y + bounds.height);
| ^
15 | };
16 | await expectInside(page.getByRole("region", { name: "나의 소리 탐험 준비" }));
17 | const start = page.getByRole("button", { name: /소리 시작/ });
at expectInside (/home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:14:45)
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:16:3
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 851.5625
Received: 1203.578125
17 | const start = page.getByRole("button", { name: /소리 시작/ });
18 | const control = (await page.locator(".volume-row").boundingBox())!;
> 19 | expect.soft(control.y + control.height).toBeLessThanOrEqual((await start.boundingBox())!.y);
| ^
20 | await volume.focus();
21 | await volume.press("ArrowRight");
22 | await expect(volume).toHaveValue("36");
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:19:43
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 402
Received: 1188
11 | expect.soft(control.x).toBeGreaterThanOrEqual(bounds.x);
12 | expect.soft(control.y).toBeGreaterThanOrEqual(bounds.y);
> 13 | expect.soft(control.x + control.width).toBeLessThanOrEqual(bounds.x + bounds.width);
| ^
14 | expect.soft(control.y + control.height).toBeLessThanOrEqual(bounds.y + bounds.height);
15 | };
16 | await expectInside(page.getByRole("region", { name: "나의 소리 탐험 준비" }));
at expectInside (/home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:13:44)
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:24:3
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 748.3125
Received: 852.3125
12 | expect.soft(control.y).toBeGreaterThanOrEqual(bounds.y);
13 | expect.soft(control.x + control.width).toBeLessThanOrEqual(bounds.x + bounds.width);
> 14 | expect.soft(control.y + control.height).toBeLessThanOrEqual(bounds.y + bounds.height);
| ^
15 | };
16 | await expectInside(page.getByRole("region", { name: "나의 소리 탐험 준비" }));
17 | const start = page.getByRole("button", { name: /소리 시작/ });
at expectInside (/home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:14:45)
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:24:3
Error Context: test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ──────────────────────
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 1250.015625
Received: 1687.84375
12 | expect.soft(control.y).toBeGreaterThanOrEqual(bounds.y);
13 | expect.soft(control.x + control.width).toBeLessThanOrEqual(bounds.x + bounds.width);
> 14 | expect.soft(control.y + control.height).toBeLessThanOrEqual(bounds.y + bounds.height);
| ^
15 | };
16 | await expectInside(page.getByRole("region", { name: "나의 소리 탐험 준비" }));
17 | const start = page.getByRole("button", { name: /소리 시작/ });
at expectInside (/home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:14:45)
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:16:3
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 1184.015625
Received: 1687.84375
17 | const start = page.getByRole("button", { name: /소리 시작/ });
18 | const control = (await page.locator(".volume-row").boundingBox())!;
> 19 | expect.soft(control.y + control.height).toBeLessThanOrEqual((await start.boundingBox())!.y);
| ^
20 | await volume.focus();
21 | await volume.press("ArrowRight");
22 | await expect(volume).toHaveValue("36");
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:19:43
Error: expect(received).toBeLessThanOrEqual(expected)
Expected: <= 169.03125
Received: 696.890625
12 | expect.soft(control.y).toBeGreaterThanOrEqual(bounds.y);
13 | expect.soft(control.x + control.width).toBeLessThanOrEqual(bounds.x + bounds.width);
> 14 | expect.soft(control.y + control.height).toBeLessThanOrEqual(bounds.y + bounds.height);
| ^
15 | };
16 | await expectInside(page.getByRole("region", { name: "나의 소리 탐험 준비" }));
17 | const start = page.getByRole("button", { name: /소리 시작/ });
at expectInside (/home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:14:45)
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:24:3
Error Context: test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ──────────────────────
[mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ───────────────────────
+15
View File
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-_gwQxvGu.css 15.23 kB │ gzip: 4.38 kB
dist/assets/index-DNw8lyKU.js 244.40 kB │ gzip: 77.01 kB
✓ built in 157ms
빌드 음성 자산 101개 확인
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 전체 음량을 시작 CTA
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed | 9 skipped (10)
Start at 05:32:46
Duration 2.00s (environment 48%, tests 30%, transform 8%, setup 8%, import 5%, worker 1%)
+77
View File
@@ -0,0 +1,77 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 전체 음량을 시작 CTA
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/App.test.tsx (10 tests | 1 failed | 9 skipped) 808ms
❯ 시작부터 결과까지 사용자 흐름 (10)
× 전체 음량을 시작 CTA 앞 설정 카드와 게임 환경 패널에 배치하고 값을 유지한다 805ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 전체 음량을 시작 CTA 앞 설정 카드와 게임 환경 패널에 배치하고 값을 유지한다
Error: expect(element).toContainElement(element)
<section
aria-labelledby="setup-title"
class="setup-card"
/> does not contain: <input
id="volume"
max="100"
min="0"
type="range"
value="35"
/>
❯ tests/App.test.tsx:53:24
51| const volume = screen.getByRole("slider", { name: "전체 음량" });
52| const start = screen.getByRole("button", { name: /소리 시작/ });
53| expect.soft(setup).toContainElement(volume);
| ^
54| // CSS 배치만 바꾸지 않고 키보드·스크린리더도 음량→시작 순서로 읽어야 한다.
55| expect.soft(volume.compareDocumentPosition(start) & Node.DOCUMENT_…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/3]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 전체 음량을 시작 CTA 앞 설정 카드와 게임 환경 패널에 배치하고 값을 유지한다
AssertionError: expected +0 not to be +0 // Object.is equality
❯ tests/App.test.tsx:55:95
53| expect.soft(setup).toContainElement(volume);
54| // CSS 배치만 바꾸지 않고 키보드·스크린리더도 음량→시작 순서로 읽어야 한다.
55| expect.soft(volume.compareDocumentPosition(start) & Node.DOCUMENT_…
| ^
56| fireEvent.change(volume, { target: { value: "20" } });
57| await f.user.click(start);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/3]⎯
FAIL tests/App.test.tsx > 시작부터 결과까지 사용자 흐름 > 전체 음량을 시작 CTA 앞 설정 카드와 게임 환경 패널에 배치하고 값을 유지한다
Error: expect(element).toContainElement(element)
<aside
class="environment-panel"
/> does not contain: <input
id="volume"
max="100"
min="0"
type="range"
value="20"
/>
❯ tests/App.test.tsx:59:52
57| await f.user.click(start);
58| const gameVolume = screen.getByRole("slider", { name: "전체 음량" });
59| expect.soft(screen.getByRole("complementary")).toContainElement(ga…
| ^
60| expect(screen.getAllByRole("slider", { name: "전체 음량" })).toHaveLen…
61| expect(gameVolume).toHaveValue("20");
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/3]⎯
Test Files 1 failed (1)
Tests 1 failed | 9 skipped (10)
Start at 05:31:48
Duration 2.03s (tests 44%, environment 38%, setup 8%, transform 6%, import 3%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx -t 시작 안내
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 3 passed | 10 skipped (13)
Start at 05:33:49
Duration 1.24s (environment 49%, tests 27%, transform 10%, setup 9%, import 4%, worker 1%)
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --grep 전체 음량
[WebServer] (node:756185) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 2 tests using 2 workers
(node:756205) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:756206) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.7s)
✓ 1 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.8s)
2 passed (3.8s)
+72
View File
@@ -0,0 +1,72 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/ui.spec.ts --grep 전체 음량
[WebServer] (node:755608) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 2 tests using 2 workers
(node:755630) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:755629) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.8s)
✘ 2 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.9s)
1) [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ─────────────────────
Error: expect(received).toBeGreaterThanOrEqual(expected)
Expected: >= 24
Received: 0
44 | const cards = (await page.locator(".game-columns").boundingBox())!;
45 | const footer = (await page.getByRole("contentinfo").boundingBox())!;
> 46 | expect(footer.y - (cards.y + cards.height)).toBeGreaterThanOrEqual(24);
| ^
47 | expect(
48 | await page.evaluate(
49 | () => document.documentElement.scrollWidth <= innerWidth,
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:46:47
Error Context: test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/trace.zip
Usage:
npx playwright show-trace test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ──────────────────────
Error: expect(received).toBeGreaterThanOrEqual(expected)
Expected: >= 24
Received: 0
44 | const cards = (await page.locator(".game-columns").boundingBox())!;
45 | const footer = (await page.getByRole("contentinfo").boundingBox())!;
> 46 | expect(footer.y - (cards.y + cards.height)).toBeGreaterThanOrEqual(24);
| ^
47 | expect(
48 | await page.evaluate(
49 | () => document.documentElement.scrollWidth <= innerWidth,
at /home/hermes/worktrees/find-word-v0.1/tests/browser/ui.spec.ts:46:47
Error Context: test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/ui-전체-음량은-설정-흐름-안에-들어가며-키보드로-조절된다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ──────────────────────
[mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ───────────────────────
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/App.test.tsx tests/release.test.ts -t 브랜드
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 2 passed (2)
Tests 2 passed | 12 skipped (14)
Start at 05:50:16
Duration 1.25s (environment 49%, setup 21%, tests 18%, transform 7%, import 3%, worker 1%)
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/release.test.ts tests/build.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 2 passed (2)
Tests 6 passed (6)
Start at 05:51:40
Duration 733ms (tests 62%, setup 34%, transform 2%, worker 2%, import 1%)
+146
View File
@@ -0,0 +1,146 @@
> find-word@0.1.0 test
> vitest run tests/release.test.ts tests/build.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/release.test.ts (3 tests | 2 failed) 23ms
❯ 릴리스 문서 계약 (3)
× 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다 18ms
× 잠금 파일의 모든 런타임 의존성 notice 전문을 배포용 사본에 보존한다 1ms
❯ tests/build.test.ts (3 tests | 2 failed) 582ms
❯ production 자산 검증기 (3)
× dist의 런타임 notice 누락을 실패시킨다 192ms
× dist의 런타임 notice 축약을 실패시킨다 192ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 4 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/build.test.ts > production 자산 검증기 > dist의 런타임 notice 누락을 실패시킨다
FAIL tests/build.test.ts > production 자산 검증기 > dist의 런타임 notice 축약을 실패시킨다
AssertionError: expected [Function verify] to throw an error
- Expected:
null
+ Received:
undefined
❯ tests/build.test.ts:47:22
45| if (damage === "누락") rmSync(notice, { force: true });
46| else writeFileSync(notice, "React / ReactDOM / scheduler — MIT L…
47| expect(verify).toThrow(/notice/i);
| ^
48| } finally {
49| rmSync(temporary, { recursive: true, force: true });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다
AssertionError: expected undefined to be 'UNLICENSED' // Object.is equality
- Expected:
"UNLICENSED"
+ Received:
undefined
❯ tests/release.test.ts:12:35
10| const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
11| expect.soft(metadata.private).toBe(true);
12| expect.soft(metadata.license).toBe("UNLICENSED");
| ^
13| expect.soft(lock.packages[""].license).toBe("UNLICENSED");
14| expect.soft(metadata.repository).toEqual({
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다
AssertionError: expected undefined to be 'UNLICENSED' // Object.is equality
- Expected:
"UNLICENSED"
+ Received:
undefined
❯ tests/release.test.ts:13:44
11| expect.soft(metadata.private).toBe(true);
12| expect.soft(metadata.license).toBe("UNLICENSED");
13| expect.soft(lock.packages[""].license).toBe("UNLICENSED");
| ^
14| expect.soft(metadata.repository).toEqual({
15| type: "git",
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다
AssertionError: expected undefined to deeply equal { type: 'git', …(1) }
- Expected:
{
"type": "git",
"url": "https://source.ibit.kr/OZ-Group/Find-Word.git",
}
+ Received:
undefined
❯ tests/release.test.ts:14:38
12| expect.soft(metadata.license).toBe("UNLICENSED");
13| expect.soft(lock.packages[""].license).toBe("UNLICENSED");
14| expect.soft(metadata.repository).toEqual({
| ^
15| type: "git",
16| url: "https://source.ibit.kr/OZ-Group/Find-Word.git",
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다
AssertionError: expected undefined to be 'https://source.ibit.kr/OZ-Group/Find-…' // Object.is equality
- Expected:
"https://source.ibit.kr/OZ-Group/Find-Word"
+ Received:
undefined
❯ tests/release.test.ts:18:36
16| url: "https://source.ibit.kr/OZ-Group/Find-Word.git",
17| });
18| expect.soft(metadata.homepage).toBe("https://source.ibit.kr/OZ-Gro…
| ^
19| const license = readFileSync("LICENSE", "utf8");
20| expect(license).toMatch(/private/i);
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 프로젝트는 비공개 권리 유보이며 저장소 주소를 명시한다
Error: ENOENT: no such file or directory, open 'LICENSE'
❯ tests/release.test.ts:19:21
17| });
18| expect.soft(metadata.homepage).toBe("https://source.ibit.kr/OZ-Gro…
19| const license = readFileSync("LICENSE", "utf8");
| ^
20| expect(license).toMatch(/private/i);
21| expect(license).toContain("All rights reserved.");
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/8]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > 잠금 파일의 모든 런타임 의존성 notice 전문을 배포용 사본에 보존한다
Error: ENOENT: no such file or directory, open 'THIRD_PARTY_NOTICES.md'
❯ tests/release.test.ts:28:20
26| packages: Record<string, { dev?: boolean; version: string }>;
27| };
28| const notice = readFileSync("THIRD_PARTY_NOTICES.md", "utf8");
| ^
29| expect(readFileSync("public/THIRD_PARTY_NOTICES.md", "utf8")).toBe…
30| for (const [path, pkg] of Object.entries(lock.packages)) {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[7/8]⎯
Test Files 2 failed (2)
Tests 4 failed | 2 passed (6)
Start at 05:51:01
Duration 884ms (tests 66%, setup 26%, transform 6%, import 1%, worker 1%)
+5
View File
@@ -0,0 +1,5 @@
> find-word@0.1.0 audio:generate
> python3 scripts/generate_audio.py
101개 WAV 생성, 합계 20,497,222 바이트
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts tests/game.test.ts tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 3 passed (3)
Tests 24 passed (24)
Start at 05:55:53
Duration 2.10s (environment 49%, tests 34%, setup 12%, transform 3%, worker 2%, import 1%)
+540
View File
@@ -0,0 +1,540 @@
> find-word@0.1.0 test
> vitest run tests/assets.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/assets.test.ts (2 tests | 1 failed) 52ms
❯ 저장한 한국어 음성 자산 (2)
× 모든 문장과 안내에 대응하는 로컬 WAV·출처·해시를 제공한다 46ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/assets.test.ts > 저장한 한국어 음성 자산 > 모든 문장과 안내에 대응하는 로컬 WAV·출처·해시를 제공한다
AssertionError: expected [ { file: 'intro.wav', …(1) }, …(100) ] to deeply equal [ { file: 'intro.wav', …(1) }, …(100) ]
- Expected
+ Received
@@ -3,404 +3,404 @@
"file": "intro.wav",
"text": "으하하하~~ 나는 에스다. 널 위한 행운의 단어를 불러주마, 잘 찾아보거라.",
},
{
"file": "luck-apple.wav",
- "text": "오늘의 행운을 담은 단어는 ‘사과’입니다.",
+ "text": "오늘의 내 행운은 사과에 있다.",
},
{
"file": "luck-sea.wav",
- "text": "오늘의 행운을 담은 단어는 ‘바다’입니다.",
+ "text": "오늘의 내 행운은 바다에 있다.",
},
{
"file": "luck-tree.wav",
- "text": "오늘의 행운을 담은 단어는 ‘나무’입니다.",
+ "text": "오늘의 내 행운은 나무에 있다.",
},
{
"file": "luck-hat.wav",
- "text": "오늘의 행운을 담은 단어는 ‘모자’입니다.",
+ "text": "오늘의 내 행운은 모자에 있다.",
},
{
"file": "luck-umbrella.wav",
- "text": "오늘의 행운을 담은 단어는 ‘우산’입니다.",
+ "text": "오늘의 내 행운은 우산에 있다.",
},
{
"file": "luck-market.wav",
- "text": "오늘의 행운을 담은 단어는 ‘시장’입니다.",
+ "text": "오늘의 내 행운은 시장에 있다.",
},
{
"file": "luck-oil.wav",
- "text": "오늘의 행운을 담은 단어는 ‘기름’입니다.",
+ "text": "오늘의 내 행운은 기름에 있다.",
},
{
"file": "luck-sound.wav",
- "text": "오늘의 행운을 담은 단어는 ‘소리’입니다.",
+ "text": "오늘의 내 행운은 소리에 있다.",
},
{
"file": "luck-meat.wav",
- "text": "오늘의 행운을 담은 단어는 ‘고기’입니다.",
+ "text": "오늘의 내 행운은 고기에 있다.",
},
{
"file": "luck-furniture.wav",
- "text": "오늘의 행운을 담은 단어는 ‘가구’입니다.",
+ "text": "오늘의 내 행운은 가구에 있다.",
},
{
"file": "password-apple.wav",
- "text": "연습 암호는 ‘다과’, 진짜 암호는 ‘사과’입니다.",
+ "text": "비밀 암호는 사과입니다.",
},
{
"file": "password-sea.wav",
- "text": "연습 암호는 ‘사다’, 진짜 암호는 ‘바다’입니다.",
+ "text": "비밀 암호는 바다입니다.",
},
{
"file": "password-tree.wav",
- "text": "연습 암호는 ‘나루’, 진짜 암호는 ‘나무’입니다.",
+ "text": "비밀 암호는 나무입니다.",
},
{
"file": "password-hat.wav",
- "text": "연습 암호는 ‘포자’, 진짜 암호는 ‘모자’입니다.",
+ "text": "비밀 암호는 모자입니다.",
},
{
"file": "password-umbrella.wav",
- "text": "연습 암호는 ‘부산’, 진짜 암호는 ‘우산’입니다.",
+ "text": "비밀 암호는 우산입니다.",
},
{
"file": "password-market.wav",
- "text": "연습 암호는 ‘지장’, 진짜 암호는 ‘시장’입니다.",
+ "text": "비밀 암호는 시장입니다.",
},
{
"file": "password-oil.wav",
- "text": "연습 암호는 ‘이름’, 진짜 암호는 ‘기름’입니다.",
+ "text": "비밀 암호는 기름입니다.",
},
{
"file": "password-sound.wav",
- "text": "연습 암호는 ‘오리’, 진짜 암호는 ‘소리’입니다.",
+ "text": "비밀 암호는 소리입니다.",
},
{
"file": "password-meat.wav",
- "text": "연습 암호는 ‘보기’, 진짜 암호는 ‘고기’입니다.",
+ "text": "비밀 암호는 고기입니다.",
},
{
"file": "password-furniture.wav",
- "text": "연습 암호는 ‘하구’, 진짜 암호는 ‘가구’입니다.",
+ "text": "비밀 암호는 가구입니다.",
},
{
"file": "remember-apple.wav",
- "text": "‘다과’. 이 단어는 잊으세요. 이제 ‘사과’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 사과입니다.",
},
{
"file": "remember-sea.wav",
- "text": "‘사다’. 이 단어는 잊으세요. 이제 ‘바다’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 바다입니다.",
},
{
"file": "remember-tree.wav",
- "text": "‘나루’. 이 단어는 잊으세요. 이제 ‘나무’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 나무입니다.",
},
{
"file": "remember-hat.wav",
- "text": "‘포자’. 이 단어는 잊으세요. 이제 ‘모자’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 모자입니다.",
},
{
"file": "remember-umbrella.wav",
- "text": "‘부산’. 이 단어는 잊으세요. 이제 ‘우산’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 우산입니다.",
},
{
"file": "remember-market.wav",
- "text": "‘지장’. 이 단어는 잊으세요. 이제 ‘시장’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 시장입니다.",
},
{
"file": "remember-oil.wav",
- "text": "‘이름’. 이 단어는 잊으세요. 이제 ‘기름’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 기름입니다.",
},
{
"file": "remember-sound.wav",
- "text": "‘오리’. 이 단어는 잊으세요. 이제 ‘소리’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 소리입니다.",
},
{
"file": "remember-meat.wav",
- "text": "‘보기’. 이 단어는 잊으세요. 이제 ‘고기’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 고기입니다.",
},
{
"file": "remember-furniture.wav",
- "text": "‘하구’. 이 단어는 잊으세요. 이제 ‘가구’, 이 단어를 기억하세요.",
+ "text": "꼭 기억할 단어는 가구입니다.",
},
{
"file": "last-apple.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘다과’, ‘사고’, ‘사과’.",
+ "text": "처음 단어는 별, 마지막 단어는 사과입니다.",
},
{
"file": "last-sea.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘사다’, ‘보다’, ‘바다’.",
+ "text": "처음 단어는 별, 마지막 단어는 바다입니다.",
},
{
"file": "last-tree.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘나루’, ‘너무’, ‘나무’.",
+ "text": "처음 단어는 별, 마지막 단어는 나무입니다.",
},
{
"file": "last-hat.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘포자’, ‘모조’, ‘모자’.",
+ "text": "처음 단어는 별, 마지막 단어는 모자입니다.",
},
{
"file": "last-umbrella.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘부산’, ‘오산’, ‘우산’.",
+ "text": "처음 단어는 별, 마지막 단어는 우산입니다.",
},
{
"file": "last-market.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘지장’, ‘사장’, ‘시장’.",
+ "text": "처음 단어는 별, 마지막 단어는 시장입니다.",
},
{
"file": "last-oil.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘이름’, ‘거름’, ‘기름’.",
+ "text": "처음 단어는 별, 마지막 단어는 기름입니다.",
},
{
"file": "last-sound.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘오리’, ‘수리’, ‘소리’.",
+ "text": "처음 단어는 별, 마지막 단어는 소리입니다.",
},
{
"file": "last-meat.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘보기’, ‘고개’, ‘고기’.",
+ "text": "처음 단어는 별, 마지막 단어는 고기입니다.",
},
{
"file": "last-furniture.wav",
- "text": "세 단어 중 마지막을 고르세요. ‘하구’, ‘가교’, ‘가구’.",
+ "text": "처음 단어는 별, 마지막 단어는 가구입니다.",
},
{
"file": "first-apple.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘사과’, ‘다과’, ‘사고’.",
+ "text": "첫 번째는 사과, 두 번째는 달입니다.",
},
{
"file": "first-sea.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘바다’, ‘사다’, ‘보다’.",
+ "text": "첫 번째는 바다, 두 번째는 달입니다.",
},
{
"file": "first-tree.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘나무’, ‘나루’, ‘너무’.",
+ "text": "첫 번째는 나무, 두 번째는 달입니다.",
},
{
"file": "first-hat.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘모자’, ‘포자’, ‘모조’.",
+ "text": "첫 번째는 모자, 두 번째는 달입니다.",
},
{
"file": "first-umbrella.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘우산’, ‘부산’, ‘오산’.",
+ "text": "첫 번째는 우산, 두 번째는 달입니다.",
},
{
"file": "first-market.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘시장’, ‘지장’, ‘사장’.",
+ "text": "첫 번째는 시장, 두 번째는 달입니다.",
},
{
"file": "first-oil.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘기름’, ‘이름’, ‘거름’.",
+ "text": "첫 번째는 기름, 두 번째는 달입니다.",
},
{
"file": "first-sound.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘소리’, ‘오리’, ‘수리’.",
+ "text": "첫 번째는 소리, 두 번째는 달입니다.",
},
{
"file": "first-meat.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘고기’, ‘보기’, ‘고개’.",
+ "text": "첫 번째는 고기, 두 번째는 달입니다.",
},
{
"file": "first-furniture.wav",
- "text": "세 단어 중 첫 번째를 고르세요. ‘가구’, ‘하구’, ‘가교’.",
+ "text": "첫 번째는 가구, 두 번째는 달입니다.",
},
{
"file": "repeat-apple.wav",
- "text": "두 번 들린 단어를 고르세요. ‘사과’, ‘다과’, ‘사과’, ‘사고’.",
+ "text": "사과. 다시 한번, 사과.",
},
{
"file": "repeat-sea.wav",
- "text": "두 번 들린 단어를 고르세요. ‘바다’, ‘사다’, ‘바다’, ‘보다’.",
+ "text": "바다. 다시 한번, 바다.",
},
{
"file": "repeat-tree.wav",
- "text": "두 번 들린 단어를 고르세요. ‘나무’, ‘나루’, ‘나무’, ‘너무’.",
+ "text": "나무. 다시 한번, 나무.",
},
{
"file": "repeat-hat.wav",
- "text": "두 번 들린 단어를 고르세요. ‘모자’, ‘포자’, ‘모자’, ‘모조’.",
+ "text": "모자. 다시 한번, 모자.",
},
{
"file": "repeat-umbrella.wav",
- "text": "두 번 들린 단어를 고르세요. ‘우산’, ‘부산’, ‘우산’, ‘오산’.",
+ "text": "우산. 다시 한번, 우산.",
},
{
"file": "repeat-market.wav",
- "text": "두 번 들린 단어를 고르세요. ‘시장’, ‘지장’, ‘시장’, ‘사장’.",
+ "text": "시장. 다시 한번, 시장.",
},
{
"file": "repeat-oil.wav",
- "text": "두 번 들린 단어를 고르세요. ‘기름’, ‘이름’, ‘기름’, ‘거름’.",
+ "text": "기름. 다시 한번, 기름.",
},
{
"file": "repeat-sound.wav",
- "text": "두 번 들린 단어를 고르세요. ‘소리’, ‘오리’, ‘소리’, ‘수리’.",
+ "text": "소리. 다시 한번, 소리.",
},
{
"file": "repeat-meat.wav",
- "text": "두 번 들린 단어를 고르세요. ‘고기’, ‘보기’, ‘고기’, ‘고개’.",
+ "text": "고기. 다시 한번, 고기.",
},
{
"file": "repeat-furniture.wav",
- "text": "두 번 들린 단어를 고르세요. ‘가구’, ‘하구’, ‘가구’, ‘가교’.",
+ "text": "가구. 다시 한번, 가구.",
},
{
"file": "correction-apple.wav",
- "text": "정답은 ‘다과’입니다. 아니, 잘못 말했어요. ‘사과’로 정정합니다.",
+ "text": "달이 아니라 사과입니다.",
},
{
"file": "correction-sea.wav",
- "text": "정답은 ‘사다’입니다. 아니, 잘못 말했어요. ‘바다’로 정정합니다.",
+ "text": "달이 아니라 바다입니다.",
},
{
"file": "correction-tree.wav",
- "text": "정답은 ‘나루’입니다. 아니, 잘못 말했어요. ‘나무’로 정정합니다.",
+ "text": "달이 아니라 나무입니다.",
},
{
"file": "correction-hat.wav",
- "text": "정답은 ‘포자’입니다. 아니, 잘못 말했어요. ‘모자’로 정정합니다.",
+ "text": "달이 아니라 모자입니다.",
},
{
"file": "correction-umbrella.wav",
- "text": "정답은 ‘부산’입니다. 아니, 잘못 말했어요. ‘우산’로 정정합니다.",
+ "text": "달이 아니라 우산입니다.",
},
{
"file": "correction-market.wav",
- "text": "정답은 ‘지장’입니다. 아니, 잘못 말했어요. ‘시장’로 정정합니다.",
+ "text": "달이 아니라 시장입니다.",
},
{
"file": "correction-oil.wav",
- "text": "정답은 ‘이름’입니다. 아니, 잘못 말했어요. ‘기름’로 정정합니다.",
+ "text": "달이 아니라 기름입니다.",
},
{
"file": "correction-sound.wav",
- "text": "정답은 ‘오리’입니다. 아니, 잘못 말했어요. ‘소리’로 정정합니다.",
+ "text": "달이 아니라 소리입니다.",
},
{
"file": "correction-meat.wav",
- "text": "정답은 ‘보기’입니다. 아니, 잘못 말했어요. ‘고기’로 정정합니다.",
+ "text": "달이 아니라 고기입니다.",
},
{
"file": "correction-furniture.wav",
- "text": "정답은 ‘하구’입니다. 아니, 잘못 말했어요. ‘가구’로 정정합니다.",
+ "text": "달이 아니라 가구입니다.",
},
{
"file": "middle-apple.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘다과’, ‘사과’, ‘사고’.",
+ "text": "별, 사과, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-sea.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘사다’, ‘바다’, ‘보다’.",
+ "text": "별, 바다, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-tree.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘나루’, ‘나무’, ‘너무’.",
+ "text": "별, 나무, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-hat.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘포자’, ‘모자’, ‘모조’.",
+ "text": "별, 모자, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-umbrella.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘부산’, ‘우산’, ‘오산’.",
+ "text": "별, 우산, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-market.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘지장’, ‘시장’, ‘사장’.",
+ "text": "별, 시장, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-oil.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘이름’, ‘기름’, ‘거름’.",
+ "text": "별, 기름, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-sound.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘오리’, ‘소리’, ‘수리’.",
+ "text": "별, 소리, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-meat.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘보기’, ‘고기’, ‘고개’.",
+ "text": "별, 고기, 달. 가운데 단어를 기억하세요.",
},
{
"file": "middle-furniture.wav",
- "text": "세 단어 중 가운데를 고르세요. ‘하구’, ‘가구’, ‘가교’.",
+ "text": "별, 가구, 달. 가운데 단어를 기억하세요.",
},
{
"file": "message-apple.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘다과’, ‘사과’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 사과입니다.",
},
{
"file": "message-sea.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘사다’, ‘바다’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 바다입니다.",
},
{
"file": "message-tree.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘나루’, ‘나무’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 나무입니다.",
},
{
"file": "message-hat.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘포자’, ‘모자’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 모자입니다.",
},
{
"file": "message-umbrella.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘부산’, ‘우산’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 우산입니다.",
},
{
"file": "message-market.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘지장’, ‘시장’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 시장입니다.",
},
{
"file": "message-oil.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘이름’, ‘기름’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 기름입니다.",
},
{
"file": "message-sound.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘오리’, ‘소리’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 소리입니다.",
},
{
"file": "message-meat.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘보기’, ‘고기’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 고기입니다.",
},
{
"file": "message-furniture.wav",
- "text": "초록 불이면 앞 단어, 빨간 불이면 뒤 단어입니다. ‘하구’, ‘가구’. 지금은 빨간 불입니다.",
+ "text": "에스가 남긴 메시지는 가구입니다.",
},
{
"file": "mission-apple.wav",
- "text": "오른쪽 표지는 ‘사과’, 왼쪽 표지는 ‘다과’입니다.",
+ "text": "이번 임무의 단서는 사과입니다.",
},
{
"file": "mission-sea.wav",
- "text": "오른쪽 표지는 ‘바다’, 왼쪽 표지는 ‘사다’입니다.",
+ "text": "이번 임무의 단서는 바다입니다.",
},
{
"file": "mission-tree.wav",
- "text": "오른쪽 표지는 ‘나무’, 왼쪽 표지는 ‘나루’입니다.",
+ "text": "이번 임무의 단서는 나무입니다.",
},
{
"file": "mission-hat.wav",
- "text": "오른쪽 표지는 ‘모자’, 왼쪽 표지는 ‘포자’입니다.",
+ "text": "이번 임무의 단서는 모자입니다.",
},
{
"file": "mission-umbrella.wav",
- "text": "오른쪽 표지는 ‘우산’, 왼쪽 표지는 ‘부산’입니다.",
+ "text": "이번 임무의 단서는 우산입니다.",
},
{
"file": "mission-market.wav",
- "text": "오른쪽 표지는 ‘시장’, 왼쪽 표지는 ‘지장’입니다.",
+ "text": "이번 임무의 단서는 시장입니다.",
},
{
"file": "mission-oil.wav",
- "text": "오른쪽 표지는 ‘기름’, 왼쪽 표지는 ‘이름’입니다.",
+ "text": "이번 임무의 단서는 기름입니다.",
},
{
"file": "mission-sound.wav",
- "text": "오른쪽 표지는 ‘소리’, 왼쪽 표지는 ‘오리’입니다.",
+ "text": "이번 임무의 단서는 소리입니다.",
},
{
"file": "mission-meat.wav",
- "text": "오른쪽 표지는 ‘고기’, 왼쪽 표지는 ‘보기’입니다.",
+ "text": "이번 임무의 단서는 고기입니다.",
},
{
"file": "mission-furniture.wav",
- "text": "오른쪽 표지는 ‘가구’, 왼쪽 표지는 ‘하구’입니다.",
+ "text": "이번 임무의 단서는 가구입니다.",
},
]
❯ tests/assets.test.ts:36:70
34| ),
35| ];
36| expect(manifest.files.map(({ file, text }) => ({ file, text }))).t…
| ^
37| expected,
38| );
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
Test Files 1 failed (1)
Tests 1 failed | 1 passed (2)
Start at 05:54:06
Duration 554ms (setup 57%, transform 22%, tests 15%, import 4%, worker 2%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/content.test.ts tests/game.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 2 passed (2)
Tests 22 passed (22)
Start at 05:54:06
Duration 1.18s (environment 80%, setup 13%, transform 3%, tests 3%, worker 1%, import 1%)
File diff suppressed because it is too large Load Diff
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/release.test.ts -t Node 최소
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 1 passed | 3 skipped (4)
Start at 05:55:53
Duration 558ms (setup 82%, transform 8%, worker 4%, tests 3%, import 2%)
+438
View File
@@ -0,0 +1,438 @@
> find-word@0.1.0 test
> vitest run tests/release.test.ts -t Node 최소
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/release.test.ts (4 tests | 1 failed | 3 skipped) 27ms
❯ 릴리스 문서 계약 (4)
× Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다 25ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: expected '>=22.12.0' to be '>=22.22.2' // Object.is equality
Expected: ">=22.22.2"
Received: ">=22.12.0"
❯ tests/release.test.ts:11:40
9| const metadata = JSON.parse(readFileSync("package.json", "utf8"));
10| const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
11| expect.soft(metadata.engines.node).toBe(">=22.22.2");
| ^
12| expect.soft(lock.packages[""].engines.node).toBe(">=22.22.2");
13| expect(lock.packages["node_modules/jsdom"].engines.node).toContain…
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: expected '>=22.12.0' to be '>=22.22.2' // Object.is equality
Expected: ">=22.22.2"
Received: ">=22.12.0"
❯ tests/release.test.ts:12:49
10| const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
11| expect.soft(metadata.engines.node).toBe(">=22.22.2");
12| expect.soft(lock.packages[""].engines.node).toBe(">=22.22.2");
| ^
13| expect(lock.packages["node_modules/jsdom"].engines.node).toContain…
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: README.md: expected '# 단어를 맞춰라 · Find-Word v0.1\n\n소음 속에 숨…' to contain '22.22.2'
- Expected
+ Received
- 22.22.2
+ # 단어를 맞춰라 · Find-Word v0.1
+
+ 소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
+
+ **Designed by OZ at Heat-T Medical Inc.**
+
+ Canonical repository: [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word).
+ 프로젝트 자체는 **private / all-rights-reserved**이며 `package.json`의
+ `license`는 `UNLICENSED`입니다. [LICENSE](LICENSE)를 참고하세요.
+ 런타임 의존성 React, ReactDOM, scheduler의 MIT 전문은
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)에 보존하며,
+ `public/THIRD_PARTY_NOTICES.md`가 빌드 시 `dist/THIRD_PARTY_NOTICES.md`로 복사됩니다.
+ 빌드 검증기는 설치된 의존성의 라이선스 전문·버전과 세 사본을 대조합니다.
+
+ React + TypeScript strict + Vite로 만든 정적 SPA입니다. 로그인, 개인정보 저장,
+ 분석 도구, 외부 CDN, 런타임 TTS/API 요청이 없습니다. 청각적 주의·식별 게임이며
+ 의료적 진단·치료나 효과를 주장하지 않습니다.
+
+ ## 실행
+
+ Node.js **22.12 이상**의 지원되는 LTS 버전을 권장합니다. `package-lock.json`을
+ 포함하므로 새 환경에서는 `npm ci`를 사용합니다. 이번 검증 환경은 Node 26.7.0입니다.
+
+ ```bash
+ npm ci
+ npm run dev
+ ```
+
+ 표시되는 `http://127.0.0.1:5173`에서 사용합니다. 소리는 사용자가 **게임 시작 · 소리 듣기**를
+ 누른 뒤에만 재생합니다. 낮은 음량부터 조절하고 헤드폰이나 스피커를 사용할 수 있습니다.
+
+ ## 플레이
+
+ - 골고루 풀기, 보기에서 선택, 직접 입력 중 선택합니다.
+ - 8개 환경을 순환하거나 하나를 지정합니다.
+ - 에스의 안내가 배경에 묻힌 뒤 목표 문장이 나옵니다. 재생 후 질문에 답합니다.
+ - 10문제 동안 2문제마다 난이도가 올라갑니다: **SNR +6, +3, 0, −3, −6 dB**.
+ 양수는 음성이 배경보다 크고, 0은 같고, 음수는 더 작다는 뜻입니다.
+ - 다시 듣기는 감점하지 않습니다. 정답은 `단계 × 100점`, 연속 정답은 최대 40점의 보너스를 받습니다.
+ - 결과에서 점수·정답률·최고 연속 정답·단계별 결과를 확인하고 다시 시작할 수 있습니다.
+ - 새로고침하면 기록이 사라집니다. 결과를 브라우저나 서버에 저장하지 않습니다.
+
+ 10개 유형 × 10개 단어의 100개 목표 문장을 저장했습니다. 각 세션은 유형과
+ 단어를 섞어 출제하며 정답 위치도 섞습니다. 사전 표제어와 변형 규칙은
+ [WORDS.md](docs/WORDS.md)에 설명했습니다.
+
+ ## 검증과 정적 빌드
+
+ ```bash
+ npm run lint
+ npm test
+ npm run build
+ npm run preview
+ ```
+
+ `lint`는 ESLint와 TypeScript 검사, `test`는 unit/component/자산/빌드 검증기
+ 테스트를 실행합니다. `build`는 `dist/`를 만들고 음성 101개가 원본 해시 그대로
+ 포함되었는지 확인합니다. 빌드에는 음성 합성 도구가 필요하지 않습니다.
+
+ 실제 오디오·360px/1280px 화면·키보드·접근성·오류 재시도 검증:
+
+ ```bash
+ # Chromium이 없는 개발 환경에서 한 번만 실행합니다.
+ npx playwright install chromium
+ npm run test:browser
+ # 이미 설치된 Chrome을 사용할 때의 대안입니다.
+ CHROME_PATH=/usr/bin/google-chrome npm run test:browser
+ ```
+
+ 브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
+ 실제 음성 재생이 끝나기를 기다려 약 1–2분이 걸립니다. 로컬 4173 포트를
+ 테스트 중에만 열고 종료합니다. 스크린샷은 `.agent/evidence/`에 남습니다.
+
+ ## 음성과 문서
+
+ 한국어 WAV는 직접 쓴 문장을 로컬 eSpeak NG로 합성했습니다. 환경음은
+ 브라우저에서 고정 seed로 합성합니다. 외부 음원이나 신경망 음성 모델을 사용하지 않습니다.
+ 생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
+ 참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
+
+ - [설계와 상태 전이](docs/ARCHITECTURE.md)
+ - [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
+ - [RED/GREEN 및 실제 검증 결과](.agent/RESULT.md)
+
+ 이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
+
❯ tests/release.test.ts:16:35
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
15| const document = readFileSync(path, "utf8");
16| expect.soft(document, path).toContain("22.22.2");
| ^
17| expect.soft(document, path).not.toContain("22.12");
18| }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: README.md: expected '# 단어를 맞춰라 · Find-Word v0.1\n\n소음 속에 숨…' not to contain '22.12'
- Expected
+ Received
- 22.12
+ # 단어를 맞춰라 · Find-Word v0.1
+
+ 소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
+
+ **Designed by OZ at Heat-T Medical Inc.**
+
+ Canonical repository: [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word).
+ 프로젝트 자체는 **private / all-rights-reserved**이며 `package.json`의
+ `license`는 `UNLICENSED`입니다. [LICENSE](LICENSE)를 참고하세요.
+ 런타임 의존성 React, ReactDOM, scheduler의 MIT 전문은
+ [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)에 보존하며,
+ `public/THIRD_PARTY_NOTICES.md`가 빌드 시 `dist/THIRD_PARTY_NOTICES.md`로 복사됩니다.
+ 빌드 검증기는 설치된 의존성의 라이선스 전문·버전과 세 사본을 대조합니다.
+
+ React + TypeScript strict + Vite로 만든 정적 SPA입니다. 로그인, 개인정보 저장,
+ 분석 도구, 외부 CDN, 런타임 TTS/API 요청이 없습니다. 청각적 주의·식별 게임이며
+ 의료적 진단·치료나 효과를 주장하지 않습니다.
+
+ ## 실행
+
+ Node.js **22.12 이상**의 지원되는 LTS 버전을 권장합니다. `package-lock.json`을
+ 포함하므로 새 환경에서는 `npm ci`를 사용합니다. 이번 검증 환경은 Node 26.7.0입니다.
+
+ ```bash
+ npm ci
+ npm run dev
+ ```
+
+ 표시되는 `http://127.0.0.1:5173`에서 사용합니다. 소리는 사용자가 **게임 시작 · 소리 듣기**를
+ 누른 뒤에만 재생합니다. 낮은 음량부터 조절하고 헤드폰이나 스피커를 사용할 수 있습니다.
+
+ ## 플레이
+
+ - 골고루 풀기, 보기에서 선택, 직접 입력 중 선택합니다.
+ - 8개 환경을 순환하거나 하나를 지정합니다.
+ - 에스의 안내가 배경에 묻힌 뒤 목표 문장이 나옵니다. 재생 후 질문에 답합니다.
+ - 10문제 동안 2문제마다 난이도가 올라갑니다: **SNR +6, +3, 0, −3, −6 dB**.
+ 양수는 음성이 배경보다 크고, 0은 같고, 음수는 더 작다는 뜻입니다.
+ - 다시 듣기는 감점하지 않습니다. 정답은 `단계 × 100점`, 연속 정답은 최대 40점의 보너스를 받습니다.
+ - 결과에서 점수·정답률·최고 연속 정답·단계별 결과를 확인하고 다시 시작할 수 있습니다.
+ - 새로고침하면 기록이 사라집니다. 결과를 브라우저나 서버에 저장하지 않습니다.
+
+ 10개 유형 × 10개 단어의 100개 목표 문장을 저장했습니다. 각 세션은 유형과
+ 단어를 섞어 출제하며 정답 위치도 섞습니다. 사전 표제어와 변형 규칙은
+ [WORDS.md](docs/WORDS.md)에 설명했습니다.
+
+ ## 검증과 정적 빌드
+
+ ```bash
+ npm run lint
+ npm test
+ npm run build
+ npm run preview
+ ```
+
+ `lint`는 ESLint와 TypeScript 검사, `test`는 unit/component/자산/빌드 검증기
+ 테스트를 실행합니다. `build`는 `dist/`를 만들고 음성 101개가 원본 해시 그대로
+ 포함되었는지 확인합니다. 빌드에는 음성 합성 도구가 필요하지 않습니다.
+
+ 실제 오디오·360px/1280px 화면·키보드·접근성·오류 재시도 검증:
+
+ ```bash
+ # Chromium이 없는 개발 환경에서 한 번만 실행합니다.
+ npx playwright install chromium
+ npm run test:browser
+ # 이미 설치된 Chrome을 사용할 때의 대안입니다.
+ CHROME_PATH=/usr/bin/google-chrome npm run test:browser
+ ```
+
+ 브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
+ 실제 음성 재생이 끝나기를 기다려 약 1–2분이 걸립니다. 로컬 4173 포트를
+ 테스트 중에만 열고 종료합니다. 스크린샷은 `.agent/evidence/`에 남습니다.
+
+ ## 음성과 문서
+
+ 한국어 WAV는 직접 쓴 문장을 로컬 eSpeak NG로 합성했습니다. 환경음은
+ 브라우저에서 고정 seed로 합성합니다. 외부 음원이나 신경망 음성 모델을 사용하지 않습니다.
+ 생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
+ 참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
+
+ - [설계와 상태 전이](docs/ARCHITECTURE.md)
+ - [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
+ - [RED/GREEN 및 실제 검증 결과](.agent/RESULT.md)
+
+ 이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
+
❯ tests/release.test.ts:17:39
15| const document = readFileSync(path, "utf8");
16| expect.soft(document, path).toContain("22.22.2");
17| expect.soft(document, path).not.toContain("22.12");
| ^
18| }
19| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: docs/DEPLOYMENT.md: expected '# 정적 배포 인계\n\n이 문서는 **Neo가 검토 후 수행할 절…' to contain '22.22.2'
- Expected
+ Received
- 22.22.2
+ # 정적 배포 인계
+
+ 이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
+ 접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
+
+ ## 산출물
+
+ Node.js 22.12 이상 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
+ `npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
+ `dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
+ Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
+
+ Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
+ 프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
+ React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
+ 반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
+
+ 현재 앱은 도메인 루트(`/`) 배포 기준이다. 하위 경로 배포 시 Vite base뿐 아니라
+ 음성 URL도 함께 변경하고 테스트해야 한다. 예정 도메인은 `findword.heart-m.com`이다.
+
+ ## Nginx 예시
+
+ 아래 root는 예시다. 실제 경로·기존 서버 블록·인증서는 Neo가 확인한다.
+ 기존 서비스 설정을 덮어쓰지 않고 검토 후 적용한다.
+
+ ```nginx
+ # 정적 릴리스 디렉터리를 가리킨다. 인증서와 HTTPS 설정은 운영자가 별도 추가한다.
+ server {
+ listen 80;
+ server_name findword.heart-m.com;
+ root /var/www/findword/current;
+ index index.html;
+
+ # 기본 MIME 정의로 JS/CSS/WAV가 올바른 타입으로 제공되게 한다.
+ include /etc/nginx/mime.types;
+ add_header X-Content-Type-Options nosniff always;
+ add_header Referrer-Policy no-referrer always;
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
+
+ # 파일명이 해시로 바뀌는 번들만 장기 캐시한다.
+ location /assets/ {
+ try_files $uri =404;
+ expires 1y;
+ }
+
+ # 음성 파일명은 재생성해도 같으므로 재검증한다. 누락 시 index.html 대신
+ # 404를 반환해야 앱이 명확한 음성 로드 오류와 재시도를 제공할 수 있다.
+ location /audio/ {
+ try_files $uri =404;
+ expires -1;
+ }
+
+ # SPA의 직접 진입·새로고침을 index.html로 받되 HTML은 재검증한다.
+ location / {
+ try_files $uri $uri/ /index.html;
+ expires -1;
+ }
+ }
+ ```
+
+ ## 인수 확인
+
+ 1. 서버에 적용하기 전 `nginx -t`, 기존 서비스 충돌, 파일 읽기 권한을 확인한다.
+ 2. TLS/HTTPS와 DNS를 운영자가 확인한다. 실제 도메인에서 시작 후 음성이 들리는지 확인한다.
+ 3. `audio/intro.wav`와 목표 WAV가 정상 응답하고 존재하지 않는 오디오는 404인지 확인한다.
+ 4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·10문제 완주를 직접 확인한다.
+ 5. 원자적으로 릴리스 디렉터리를 전환하고 이전 디렉터리를 보관해 되돌릴 수 있게 한다.
+
+ 오프라인 캐시를 제공하는 PWA는 아니다. 정적 자산의 최초 로드에는 해당 서버
+ 연결이 필요하다. 페이지·오디오 캐시가 서로 다른 버전으로 섞이지 않게 HTML과
+ 음성의 재검증 정책을 유지한다.
+
❯ tests/release.test.ts:16:35
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
15| const document = readFileSync(path, "utf8");
16| expect.soft(document, path).toContain("22.22.2");
| ^
17| expect.soft(document, path).not.toContain("22.12");
18| }
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
AssertionError: docs/DEPLOYMENT.md: expected '# 정적 배포 인계\n\n이 문서는 **Neo가 검토 후 수행할 절…' not to contain '22.12'
- Expected
+ Received
- 22.12
+ # 정적 배포 인계
+
+ 이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
+ 접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
+
+ ## 산출물
+
+ Node.js 22.12 이상 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
+ `npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
+ `dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
+ Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
+
+ Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
+ 프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
+ React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
+ 반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
+
+ 현재 앱은 도메인 루트(`/`) 배포 기준이다. 하위 경로 배포 시 Vite base뿐 아니라
+ 음성 URL도 함께 변경하고 테스트해야 한다. 예정 도메인은 `findword.heart-m.com`이다.
+
+ ## Nginx 예시
+
+ 아래 root는 예시다. 실제 경로·기존 서버 블록·인증서는 Neo가 확인한다.
+ 기존 서비스 설정을 덮어쓰지 않고 검토 후 적용한다.
+
+ ```nginx
+ # 정적 릴리스 디렉터리를 가리킨다. 인증서와 HTTPS 설정은 운영자가 별도 추가한다.
+ server {
+ listen 80;
+ server_name findword.heart-m.com;
+ root /var/www/findword/current;
+ index index.html;
+
+ # 기본 MIME 정의로 JS/CSS/WAV가 올바른 타입으로 제공되게 한다.
+ include /etc/nginx/mime.types;
+ add_header X-Content-Type-Options nosniff always;
+ add_header Referrer-Policy no-referrer always;
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
+
+ # 파일명이 해시로 바뀌는 번들만 장기 캐시한다.
+ location /assets/ {
+ try_files $uri =404;
+ expires 1y;
+ }
+
+ # 음성 파일명은 재생성해도 같으므로 재검증한다. 누락 시 index.html 대신
+ # 404를 반환해야 앱이 명확한 음성 로드 오류와 재시도를 제공할 수 있다.
+ location /audio/ {
+ try_files $uri =404;
+ expires -1;
+ }
+
+ # SPA의 직접 진입·새로고침을 index.html로 받되 HTML은 재검증한다.
+ location / {
+ try_files $uri $uri/ /index.html;
+ expires -1;
+ }
+ }
+ ```
+
+ ## 인수 확인
+
+ 1. 서버에 적용하기 전 `nginx -t`, 기존 서비스 충돌, 파일 읽기 권한을 확인한다.
+ 2. TLS/HTTPS와 DNS를 운영자가 확인한다. 실제 도메인에서 시작 후 음성이 들리는지 확인한다.
+ 3. `audio/intro.wav`와 목표 WAV가 정상 응답하고 존재하지 않는 오디오는 404인지 확인한다.
+ 4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·10문제 완주를 직접 확인한다.
+ 5. 원자적으로 릴리스 디렉터리를 전환하고 이전 디렉터리를 보관해 되돌릴 수 있게 한다.
+
+ 오프라인 캐시를 제공하는 PWA는 아니다. 정적 자산의 최초 로드에는 해당 서버
+ 연결이 필요하다. 페이지·오디오 캐시가 서로 다른 버전으로 섞이지 않게 HTML과
+ 음성의 재검증 정책을 유지한다.
+
❯ tests/release.test.ts:17:39
15| const document = readFileSync(path, "utf8");
16| expect.soft(document, path).toContain("22.22.2");
17| expect.soft(document, path).not.toContain("22.12");
| ^
18| }
19| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯
Test Files 1 failed (1)
Tests 1 failed | 3 skipped (4)
Start at 05:54:57
Duration 357ms (setup 64%, transform 18%, tests 14%, worker 2%, import 2%)
+12
View File
@@ -0,0 +1,12 @@
> find-word@0.1.0 test
> vitest run tests/playwright-config.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 1 passed (1)
Tests 5 passed (5)
Start at 05:55:53
Duration 3.51s (tests 89%, setup 10%, transform 1%)
+78
View File
@@ -0,0 +1,78 @@
> find-word@0.1.0 test
> vitest run tests/playwright-config.test.ts
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
❯ tests/playwright-config.test.ts (5 tests | 5 failed) 2254ms
❯ 브라우저 sandbox 기본 정책 (5)
× opt-in=undefined이면 sandbox를 켠다 472ms
× opt-in=이면 sandbox를 켠다 441ms
× opt-in=0이면 sandbox를 켠다 447ms
× opt-in=false이면 sandbox를 켠다 450ms
× 명시적인 PLAYWRIGHT_NO_SANDBOX=1에서만 sandbox를 끈다 443ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 5 ⎯⎯⎯⎯⎯⎯⎯
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=undefined이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=0이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=false이면 sandbox를 켠다
AssertionError: expected undefined to be true // Object.is equality
- Expected:
true
+ Received:
undefined
❯ tests/playwright-config.test.ts:22:42
20| it.each([undefined, "", "0", "false"])("opt-in=%s이면 sandbox를 켠다", (v…
21| const options = launchOptions(value);
22| expect.soft(options.chromiumSandbox).toBe(true);
| ^
23| expect.soft(options.args ?? []).not.toContain("--no-sandbox");
24| expect(options.executablePath).toBe("/usr/bin/google-chrome");
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/9]⎯
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=undefined이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=0이면 sandbox를 켠다
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > opt-in=false이면 sandbox를 켠다
AssertionError: expected [ '--no-sandbox' ] to not include '--no-sandbox'
❯ tests/playwright-config.test.ts:23:41
21| const options = launchOptions(value);
22| expect.soft(options.chromiumSandbox).toBe(true);
23| expect.soft(options.args ?? []).not.toContain("--no-sandbox");
| ^
24| expect(options.executablePath).toBe("/usr/bin/google-chrome");
25| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/9]⎯
FAIL tests/playwright-config.test.ts > 브라우저 sandbox 기본 정책 > 명시적인 PLAYWRIGHT_NO_SANDBOX=1에서만 sandbox를 끈다
AssertionError: expected undefined to be false // Object.is equality
- Expected:
false
+ Received:
undefined
❯ tests/playwright-config.test.ts:27:48
25| });
26| it("명시적인 PLAYWRIGHT_NO_SANDBOX=1에서만 sandbox를 끈다", () => {
27| expect(launchOptions("1").chromiumSandbox).toBe(false);
| ^
28| });
29| });
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/9]⎯
Test Files 1 failed (1)
Tests 5 failed (5)
Start at 05:54:57
Duration 2.57s (tests 93%, setup 5%, transform 1%)
+162
View File
@@ -0,0 +1,162 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/release.spec.ts --project=mobile
[WebServer] (node:763846) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 1 test using 1 worker
(node:763874) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (1.7s)
1) [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 ──────────
Error: expect(locator).toBeVisible() failed
Locator: getByText('Designed by OZ at Heat-T Medical Inc.', { exact: true })
Expected: visible
Timeout: 1000ms
Error: element(s) not found
Call log:
- Expect "soft toBeVisible" getByText('Designed by OZ at Heat-T Medical Inc.', { exact: true }) with timeout 1000ms
- waiting for getByText('Designed by OZ at Heat-T Medical Inc.', { exact: true })
7 | page,
8 | request,
> 9 | }) => {
| ^
10 | await page.goto("/");
11 | await expect
12 | .soft(
at /home/hermes/worktrees/find-word-v0.1/tests/browser/release.spec.ts:9:6
Error: expect(received).toBe(expected) // Object.is equality
- Expected - 77
+ Received + 20
- # Third-party notices
-
- Find-Word is private / proprietary; all rights reserved for original project
- materials. The following runtime components retain their own MIT licenses.
- These notices must accompany the static distribution.
-
- ## react 19.3.0
-
- MIT License
-
- Copyright (c) Meta Platforms, Inc. and affiliates.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
- ## react-dom 19.3.0
-
- MIT License
-
- Copyright (c) Meta Platforms, Inc. and affiliates.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
- ## scheduler 0.28.0
-
- MIT License
-
- Copyright (c) Meta Platforms, Inc. and affiliates.
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
+ <!doctype html>
+ <!-- 문서 언어를 한국어로 명시하여 보조 기기의 발음과 줄바꿈 기준을 맞춘다.
+ 외부 폰트·CDN을 사용하지 않으며 모든 실행 자산은 같은 출처에서 제공한다. -->
+ <html lang="ko">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta name="theme-color" content="#f8f9f4" />
+ <meta
+ name="description"
+ content="소음 속 작은 한국어 단어를 찾는 청각적 주의·식별 게임. 에스와 함께 나만의 속도로 소리를 탐험하세요."
+ />
+ <title>단어를 맞춰라 · Find-Word v0.1</title>
+ <script type="module" crossorigin src="/assets/index-JDsKSYsp.js"></script>
+ <link rel="stylesheet" crossorigin href="/assets/index-KT6VDfPn.css">
+ </head>
+ <body>
+ <div id="root"></div>
+ </body>
+ </html>
↵
10 | await page.goto("/");
11 | await expect
> 12 | .soft(
| ^
13 | page.getByText("Designed by OZ at Heat-T Medical Inc.", { exact: true }),
14 | )
15 | .toBeVisible({ timeout: 1000 });
at /home/hermes/worktrees/find-word-v0.1/tests/browser/release.spec.ts:12:33
Error Context: test-results/release-릴리스는-사용자-크레딧과-런타임-MIT-notice-전문을-제공한다-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/release-릴리스는-사용자-크레딧과-런타임-MIT-notice-전문을-제공한다-mobile/trace.zip
Usage:
npx playwright show-trace test-results/release-릴리스는-사용자-크레딧과-런타임-MIT-notice-전문을-제공한다-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
1 failed
[mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 ───────────
+11
View File
@@ -0,0 +1,11 @@
Measured with Python wave: frames / sample rate, public/audio/*.wav
WAV count: 101
intro.wav: 8.1205 s
Longest target: message-market.wav: 10.1725 s
AudioEngine scheduling: start +0.05 s; intro-to-target gap +0.35 s
Maximum scheduled intro + target: 18.6930 s
Old assertion headroom (20 s): 1.3070 s
New assertion headroom (30 s): 11.3070 s
Product timeouts remain: preparation 15000 ms; playback scheduled end + 5 s.
Retry control flow: failed play does not set introDone; retry includes intro;
target.onended -> task.finish -> play resolves -> ENDED -> answer group visible.
@@ -0,0 +1,33 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:770976) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 12 tests using 2 workers
(node:770997) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:770996) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (19.9s)
✓ 3 [desktop] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (317ms)
✓ 4 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.1s)
✓ 5 [desktop] › tests/browser/ui.spec.ts:54:1 › 난이도 설명은 모바일에서도 보인다 (823ms)
✓ 6 [desktop] › tests/browser/ui.spec.ts:74:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (17.7s)
(node:771406) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.5m)
(node:771655) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 8 [mobile] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.8s)
✓ 9 [mobile] › tests/browser/release.spec.ts:6:1 › 릴리스는 사용자 크레딧과 런타임 MIT notice 전문을 제공한다 (310ms)
✓ 10 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (948ms)
✓ 11 [mobile] › tests/browser/ui.spec.ts:54:1 › 난이도 설명은 모바일에서도 보인다 (781ms)
✓ 12 [mobile] › tests/browser/ui.spec.ts:74:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (17.2s)
✓ 7 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (1.5m)
12 passed (2.2m)
Exit code: 0
+18
View File
@@ -0,0 +1,18 @@
$ npm run build
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-KT6VDfPn.css 15.25 kB │ gzip: 4.39 kB
dist/assets/index-BJ9zMFYJ.js 245.34 kB │ gzip: 77.26 kB
✓ built in 163ms
빌드 음성 자산 101개 및 런타임 notice 전문 확인
Exit code: 0
+7
View File
@@ -0,0 +1,7 @@
$ npm run lint
> find-word@0.1.0 lint
> eslint . && tsc --noEmit
Exit code: 0
+19
View File
@@ -0,0 +1,19 @@
$ npm test
> find-word@0.1.0 test
> vitest run
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 9 passed (9)
Tests 57 passed (57)
Start at 06:11:14
Duration 7.00s (tests 67%, environment 25%, setup 7%, import 1%)
Isolate 9 workers spawned · ~662ms startup each (spawn + environment, per file)
at least ~1.32s faster with isolate: false — reuses workers across files instead of one per file
Exit code: 0
+27
View File
@@ -0,0 +1,27 @@
Worktree snapshot comparison (SHA-256; ignored build/cache/dependency outputs excluded)
Existing files changed: .agent/RESULT.md, tests/browser/game.spec.ts
New evidence logs: .agent/evidence/24-retry-audio-budget.log, .agent/evidence/24-retry-final-browser.log, .agent/evidence/24-retry-final-build.log, .agent/evidence/24-retry-final-lint.log, .agent/evidence/24-retry-final-test.log, .agent/evidence/24-retry-targeted-green.log
Removed files: none
src/, public/, dependencies, Playwright config, all other existing files: unchanged
Historical release screenshots: restored byte-for-byte from pre-run copies
git diff --check: exit 0 (existing source tree was untracked)
Changed text files: no trailing whitespace
RESULT evidence links checked: 114
Test patch:
--- .cache/retry-timeout/game.spec.before.ts 2026-10-14 05:57:19.919982148 +0000
+++ tests/browser/game.spec.ts 2026-10-14 06:10:58.987220682 +0000
@@ -137,7 +137,11 @@
);
await page.unroute("**/audio/*.wav");
await page.getByRole("button", { name: "다시 시도" }).click();
- await expect(page.getByRole("group", { name: "답 선택" })).toBeVisible();
+ // 재시도는 안내부터 재생한다. 최대 8.1205 + 10.1725 + 시작·전환 0.4초에
+ // 로드·디코드·병렬 브라우저 스케줄링 여유를 더해 이 assertion만 30초로 제한한다.
+ await expect(page.getByRole("group", { name: "답 선택" })).toBeVisible({
+ timeout: 30_000,
+ });
await page.getByRole("button", { name: "처음으로" }).click();
await expect(
page.getByRole("button", { name: "게임 시작 · 소리 듣기" }),
Exit code: 0
@@ -0,0 +1,22 @@
> find-word@0.1.0 test:browser
> playwright test tests/browser/game.spec.ts --project=desktop --workers=1 --repeat-each=3 --grep 오디오 HTTP 실패
[WebServer] (node:770385) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 3 tests using 1 worker
(node:770405) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (17.7s)
(node:770579) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (15.5s)
(node:770757) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 3 [desktop] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (16.8s)
3 passed (52.9s)
Exit code: 0
+6
View File
@@ -0,0 +1,6 @@
> find-word@0.1.0 audio:generate
> python3 scripts/generate_audio.py
101개 WAV 생성, 합계 10,864,582 바이트
재생성 검증: 101개 WAV의 원문·크기·SHA-256 모두 이전과 동일
+23
View File
@@ -0,0 +1,23 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:746238) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 4 tests using 2 workers
(node:746258) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:746259) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:95:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.4s)
(node:746526) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (56.6s)
(node:746780) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 4 [mobile] › tests/browser/game.spec.ts:95:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (12.1s)
✓ 3 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (56.0s)
4 passed (1.2m)
+234
View File
@@ -0,0 +1,234 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:743569) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 4 tests using 2 workers
(node:743591) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:743590) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (2.4s)
(node:743836) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 3 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (2.0s)
(node:743961) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:58:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (16.3s)
✓ 4 [mobile] › tests/browser/game.spec.ts:58:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (12.9s)
1) [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ───────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 93
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#eef2e4",
+ "contrastRatio": 3.75,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#6e805d",
+ "fontSize": "9.8pt (13px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 3.75 (foreground color: #6e805d, background color: #eef2e4, font size: 9.8pt (13px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<span class=\"section-number\">01</span>",
+ "target": Array [
+ ".section-number",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 3.75 (foreground color: #6e805d, background color: #eef2e4, font size: 9.8pt (13px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span class=\"section-number\">01</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".section-number",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e9",
+ "contrastRatio": 4.07,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#707967",
+ "fontSize": "7.5pt (10px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.07 (foreground color: #707967, background color: #f0f4e9, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<label class=\"mode-option selected\"><input aria-label=\"골고루 풀기\" type=\"radio\" value=\"mixed\" checked=\"\" name=\"mode\" style=\"\"><span class=\"mode-icon\" aria-hidden=\"true\">◈</span><strong>골고루 풀기</strong><small>선택 + 직접 입력</small></label>",
+ "target": Array [
+ ".selected",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.07 (foreground color: #707967, background color: #f0f4e9, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<small>선택 + 직접 입력</small>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".selected > small",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
19 | expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
20 | await page.screenshot({ path: `.agent/evidence/${info.project.name}-home.png`, fullPage: true });
> 21 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
22 | await page.emulateMedia({ reducedMotion: 'reduce' });
23 | await page.getByRole('button', { name: /소리 시작/ }).click();
24 | await expect(page.getByRole('button', { name: /듣는 중/ })).toBeDisabled();
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:21:65
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 58
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e9",
+ "contrastRatio": 4.07,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#707967",
+ "fontSize": "6.8pt (9px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.07 (foreground color: #707967, background color: #f0f4e9, font size: 6.8pt (9px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<label class=\"mode-option selected\"><input aria-label=\"골고루 풀기\" type=\"radio\" value=\"mixed\" checked=\"\" name=\"mode\" style=\"\"><span class=\"mode-icon\" aria-hidden=\"true\">◈</span><strong>골고루 풀기</strong><small>선택 + 직접 입력</small></label>",
+ "target": Array [
+ ".selected",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.07 (foreground color: #707967, background color: #f0f4e9, font size: 6.8pt (9px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<small>선택 + 직접 입력</small>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".selected > small",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
19 | expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
20 | await page.screenshot({ path: `.agent/evidence/${info.project.name}-home.png`, fullPage: true });
> 21 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
22 | await page.emulateMedia({ reducedMotion: 'reduce' });
23 | await page.getByRole('button', { name: /소리 시작/ }).click();
24 | await expect(page.getByRole('button', { name: /듣는 중/ })).toBeDisabled();
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:21:65
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
[mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ─────────────────
2 passed (21.4s)
+269
View File
@@ -0,0 +1,269 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:744290) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 4 tests using 2 workers
(node:744311) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:744312) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:58:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.5s)
(node:744575) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (16.2s)
(node:744703) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 4 [mobile] › tests/browser/game.spec.ts:58:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (12.7s)
✘ 3 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (15.3s)
1) [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ───────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 93
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#edf1e4",
+ "contrastRatio": 4.08,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#69795a",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.08 (foreground color: #69795a, background color: #edf1e4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<span>객관식</span>",
+ "target": Array [
+ ".question-meta > span:nth-child(2)",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.08 (foreground color: #69795a, background color: #edf1e4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>객관식</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".question-meta > span:nth-child(2)",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#fffefa",
+ "contrastRatio": 4.41,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#707b65",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.41 (foreground color: #707b65, background color: #fffefa, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"question-panel panel\">",
+ "target": Array [
+ ".question-panel",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.41 (foreground color: #707b65, background color: #fffefa, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<small>다시 들어도 점수는 줄어들지 않아요.</small>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "small",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
32 | expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
33 | await page.screenshot({ path: `.agent/evidence/${info.project.name}-game.png`, fullPage: true });
> 34 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
35 | await page.getByRole('button', { name: /다시 듣기/ }).click();
36 | await expect(target).toBeVisible();
37 | }
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:34:69
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 93
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#edf1e4",
+ "contrastRatio": 4.08,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#69795a",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.08 (foreground color: #69795a, background color: #edf1e4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<span>객관식</span>",
+ "target": Array [
+ ".question-meta > span:nth-child(2)",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.08 (foreground color: #69795a, background color: #edf1e4, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>객관식</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".question-meta > span:nth-child(2)",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#fffefa",
+ "contrastRatio": 4.41,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#707b65",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.41 (foreground color: #707b65, background color: #fffefa, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"question-panel panel\">",
+ "target": Array [
+ ".question-panel",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.41 (foreground color: #707b65, background color: #fffefa, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<small>다시 들어도 점수는 줄어들지 않아요.</small>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "small",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
32 | expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
33 | await page.screenshot({ path: `.agent/evidence/${info.project.name}-game.png`, fullPage: true });
> 34 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
35 | await page.getByRole('button', { name: /다시 듣기/ }).click();
36 | await expect(target).toBeVisible();
37 | }
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:34:69
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
[mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ─────────────────
2 passed (32.5s)
+339
View File
@@ -0,0 +1,339 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:745155) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 4 tests using 2 workers
(node:745175) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:745176) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:95:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.5s)
(node:745459) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (56.8s)
(node:745658) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✘ 3 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (56.0s)
✓ 4 [mobile] › tests/browser/game.spec.ts:95:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.2s)
1) [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ───────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 128
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>총 점수</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".stats > div:nth-child(1) > span",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>정답률 · 10/10</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".stats > div:nth-child(2) > span",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "8.3pt (11px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 8.3pt (11px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>최고 연속 정답</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "div:nth-child(3) > span",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
86 | fullPage: true,
87 | });
> 88 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
89 | await page.getByRole("button", { name: "다시 시작" }).click();
90 | await expect(page.getByRole("button", { name: /소리 시작/ })).toBeVisible();
91 | expect(external).toEqual([]);
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:88:65
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-desktop/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2) [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
Error: expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 128
- Array []
+ Array [
+ Object {
+ "description": "Ensure the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
+ "help": "Elements must meet minimum color contrast ratio thresholds",
+ "helpUrl": "https://dequeuniversity.com/rules/axe/4.13/color-contrast?application=playwright",
+ "id": "color-contrast",
+ "impact": "serious",
+ "nodes": Array [
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "7.5pt (10px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>총 점수</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".stats > div:nth-child(1) > span",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "7.5pt (10px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>정답률 · 10/10</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ ".stats > div:nth-child(2) > span",
+ ],
+ },
+ Object {
+ "all": Array [],
+ "any": Array [
+ Object {
+ "data": Object {
+ "bgColor": "#f0f4e8",
+ "contrastRatio": 4.38,
+ "expectedContrastRatio": "4.5:1",
+ "fgColor": "#65765a",
+ "fontSize": "7.5pt (10px)",
+ "fontWeight": "normal",
+ "messageKey": null,
+ },
+ "id": "color-contrast",
+ "impact": "serious",
+ "message": "Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "relatedNodes": Array [
+ Object {
+ "html": "<div class=\"stats\"><div><strong>3,300</strong><span>총 점수</span></div><div><strong>100%</strong><span>정답률 · 10/10</span></div><div><strong>10<small>회</small></strong><span>최고 연속 정답</span></div></div>",
+ "target": Array [
+ ".stats",
+ ],
+ },
+ ],
+ },
+ ],
+ "failureSummary": "Fix any of the following:
+ Element has insufficient color contrast of 4.38 (foreground color: #65765a, background color: #f0f4e8, font size: 7.5pt (10px), font weight: normal). Expected contrast ratio of 4.5:1",
+ "html": "<span>최고 연속 정답</span>",
+ "impact": "serious",
+ "none": Array [],
+ "target": Array [
+ "div:nth-child(3) > span",
+ ],
+ },
+ ],
+ "tags": Array [
+ "cat.color",
+ "wcag2aa",
+ "wcag143",
+ "TTv5",
+ "TT13.c",
+ "EN-301-549",
+ "EN-9.1.4.3",
+ "ACT",
+ "RGAAv4",
+ "RGAA-3.2.1",
+ ],
+ },
+ ]
86 | fullPage: true,
87 | });
> 88 | expect((await new AxeBuilder({ page }).analyze()).violations).toEqual([]);
| ^
89 | await page.getByRole("button", { name: "다시 시작" }).click();
90 | await expect(page.getByRole("button", { name: /소리 시작/ })).toBeVisible();
91 | expect(external).toEqual([]);
at /home/hermes/worktrees/find-word-v0.1/tests/browser/game.spec.ts:88:65
Error Context: test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/error-context.md
attachment #2: trace (application/zip) ─────────────────────────────────────────────────────────
test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
Usage:
npx playwright show-trace test-results/game-실제-오디오로-혼합-모드-완주·레이아웃·접근성·외부-요청-없음-mobile/trace.zip
────────────────────────────────────────────────────────────────────────────────────────────────
2 failed
[desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ────────────────
[mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 ─────────────────
2 passed (1.2m)
+181
View File
@@ -0,0 +1,181 @@
# Git commit/push 없음. 보존한 입력 문서와 새 작성 파일 목록.
PRESERVED .agent/ACCEPTANCE.md
PRESERVED .agent/CONTEXT.md
ADDED .agent/RESULT.md
PRESERVED .agent/TASK.md
ADDED .agent/evidence/01-content-green.log
ADDED .agent/evidence/01-content-red.log
ADDED .agent/evidence/02-game-green.log
ADDED .agent/evidence/02-game-red.log
ADDED .agent/evidence/03-dsp-green.log
ADDED .agent/evidence/03-dsp-intermediate.log
ADDED .agent/evidence/03-dsp-red.log
ADDED .agent/evidence/04-audio-green.log
ADDED .agent/evidence/04-audio-red.log
ADDED .agent/evidence/05-assets-red.log
ADDED .agent/evidence/06-ui-05-assets-green.log
ADDED .agent/evidence/06-ui-intermediate.log
ADDED .agent/evidence/06-ui-red.log
ADDED .agent/evidence/07-build-green.log
ADDED .agent/evidence/07-build-red.log
ADDED .agent/evidence/08-rule-green.log
ADDED .agent/evidence/08-rule-red.log
ADDED .agent/evidence/09-ime-green.log
ADDED .agent/evidence/09-ime-red.log
ADDED .agent/evidence/10-ime-reset-green.log
ADDED .agent/evidence/10-ime-reset-red.log
ADDED .agent/evidence/11-provenance-green.log
ADDED .agent/evidence/11-provenance-red.log
ADDED .agent/evidence/12-ime-replay-green.log
ADDED .agent/evidence/12-ime-replay-red.log
ADDED .agent/evidence/audio-reproduce.log
ADDED .agent/evidence/browser-final.log
ADDED .agent/evidence/browser-first.log
ADDED .agent/evidence/browser-second.log
ADDED .agent/evidence/browser-third.log
ADDED .agent/evidence/changed-files.txt
ADDED .agent/evidence/desktop-game.png
ADDED .agent/evidence/desktop-home.png
ADDED .agent/evidence/desktop-results.png
ADDED .agent/evidence/final-audit.log
ADDED .agent/evidence/final-browser.log
ADDED .agent/evidence/final-build.log
ADDED .agent/evidence/final-lint.log
ADDED .agent/evidence/final-security.log
ADDED .agent/evidence/final-test.log
ADDED .agent/evidence/mobile-game.png
ADDED .agent/evidence/mobile-home.png
ADDED .agent/evidence/mobile-results.png
ADDED .gitignore
ADDED README.md
ADDED docs/ARCHITECTURE.md
ADDED docs/AUDIO_PROVENANCE.md
ADDED docs/DEPLOYMENT.md
ADDED docs/WORDS.md
ADDED eslint.config.js
ADDED index.html
ADDED package-lock.json
ADDED package.json
ADDED playwright.config.ts
ADDED public/audio/correction-apple.wav
ADDED public/audio/correction-furniture.wav
ADDED public/audio/correction-hat.wav
ADDED public/audio/correction-market.wav
ADDED public/audio/correction-meat.wav
ADDED public/audio/correction-oil.wav
ADDED public/audio/correction-sea.wav
ADDED public/audio/correction-sound.wav
ADDED public/audio/correction-tree.wav
ADDED public/audio/correction-umbrella.wav
ADDED public/audio/first-apple.wav
ADDED public/audio/first-furniture.wav
ADDED public/audio/first-hat.wav
ADDED public/audio/first-market.wav
ADDED public/audio/first-meat.wav
ADDED public/audio/first-oil.wav
ADDED public/audio/first-sea.wav
ADDED public/audio/first-sound.wav
ADDED public/audio/first-tree.wav
ADDED public/audio/first-umbrella.wav
ADDED public/audio/intro.wav
ADDED public/audio/last-apple.wav
ADDED public/audio/last-furniture.wav
ADDED public/audio/last-hat.wav
ADDED public/audio/last-market.wav
ADDED public/audio/last-meat.wav
ADDED public/audio/last-oil.wav
ADDED public/audio/last-sea.wav
ADDED public/audio/last-sound.wav
ADDED public/audio/last-tree.wav
ADDED public/audio/last-umbrella.wav
ADDED public/audio/luck-apple.wav
ADDED public/audio/luck-furniture.wav
ADDED public/audio/luck-hat.wav
ADDED public/audio/luck-market.wav
ADDED public/audio/luck-meat.wav
ADDED public/audio/luck-oil.wav
ADDED public/audio/luck-sea.wav
ADDED public/audio/luck-sound.wav
ADDED public/audio/luck-tree.wav
ADDED public/audio/luck-umbrella.wav
ADDED public/audio/manifest.json
ADDED public/audio/message-apple.wav
ADDED public/audio/message-furniture.wav
ADDED public/audio/message-hat.wav
ADDED public/audio/message-market.wav
ADDED public/audio/message-meat.wav
ADDED public/audio/message-oil.wav
ADDED public/audio/message-sea.wav
ADDED public/audio/message-sound.wav
ADDED public/audio/message-tree.wav
ADDED public/audio/message-umbrella.wav
ADDED public/audio/middle-apple.wav
ADDED public/audio/middle-furniture.wav
ADDED public/audio/middle-hat.wav
ADDED public/audio/middle-market.wav
ADDED public/audio/middle-meat.wav
ADDED public/audio/middle-oil.wav
ADDED public/audio/middle-sea.wav
ADDED public/audio/middle-sound.wav
ADDED public/audio/middle-tree.wav
ADDED public/audio/middle-umbrella.wav
ADDED public/audio/mission-apple.wav
ADDED public/audio/mission-furniture.wav
ADDED public/audio/mission-hat.wav
ADDED public/audio/mission-market.wav
ADDED public/audio/mission-meat.wav
ADDED public/audio/mission-oil.wav
ADDED public/audio/mission-sea.wav
ADDED public/audio/mission-sound.wav
ADDED public/audio/mission-tree.wav
ADDED public/audio/mission-umbrella.wav
ADDED public/audio/password-apple.wav
ADDED public/audio/password-furniture.wav
ADDED public/audio/password-hat.wav
ADDED public/audio/password-market.wav
ADDED public/audio/password-meat.wav
ADDED public/audio/password-oil.wav
ADDED public/audio/password-sea.wav
ADDED public/audio/password-sound.wav
ADDED public/audio/password-tree.wav
ADDED public/audio/password-umbrella.wav
ADDED public/audio/remember-apple.wav
ADDED public/audio/remember-furniture.wav
ADDED public/audio/remember-hat.wav
ADDED public/audio/remember-market.wav
ADDED public/audio/remember-meat.wav
ADDED public/audio/remember-oil.wav
ADDED public/audio/remember-sea.wav
ADDED public/audio/remember-sound.wav
ADDED public/audio/remember-tree.wav
ADDED public/audio/remember-umbrella.wav
ADDED public/audio/repeat-apple.wav
ADDED public/audio/repeat-furniture.wav
ADDED public/audio/repeat-hat.wav
ADDED public/audio/repeat-market.wav
ADDED public/audio/repeat-meat.wav
ADDED public/audio/repeat-oil.wav
ADDED public/audio/repeat-sea.wav
ADDED public/audio/repeat-sound.wav
ADDED public/audio/repeat-tree.wav
ADDED public/audio/repeat-umbrella.wav
ADDED scripts/check-build.mjs
ADDED scripts/generate_audio.py
ADDED src/App.tsx
ADDED src/audio/dsp.ts
ADDED src/audio/engine.ts
ADDED src/content.ts
ADDED src/game.ts
ADDED src/main.tsx
ADDED src/styles.css
ADDED tests/App.test.tsx
ADDED tests/assets.test.ts
ADDED tests/audio.test.ts
ADDED tests/browser/game.spec.ts
ADDED tests/build.test.ts
ADDED tests/content.test.ts
ADDED tests/dsp.test.ts
ADDED tests/game.test.ts
ADDED tests/setup.ts
ADDED tsconfig.json
ADDED vite.config.ts
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

+1
View File
@@ -0,0 +1 @@
found 0 vulnerabilities
+23
View File
@@ -0,0 +1,23 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:748800) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 4 tests using 2 workers
(node:748834) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:748833) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:99:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (16.1s)
(node:749105) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (57.2s)
(node:749299) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 4 [mobile] › tests/browser/game.spec.ts:99:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (12.8s)
✓ 3 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (54.9s)
4 passed (1.2m)
+15
View File
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-BEQhGx9x.css 14.87 kB │ gzip: 4.29 kB
dist/assets/index-ZqUFJyNr.js 244.26 kB │ gzip: 76.95 kB
✓ built in 154ms
빌드 음성 자산 101개 확인
+3
View File
@@ -0,0 +1,3 @@
> find-word@0.1.0 lint
> eslint . && tsc --noEmit
+4
View File
@@ -0,0 +1,4 @@
Secret pattern scan: 34 text files, 0 matches.
Korean source explanations: 22 files, 0 missing.
Python generator syntax: PASS.
Scope: source, tests, scripts, docs, root metadata, .agent requirements; dependency binaries and PCM excluded.
+16
View File
@@ -0,0 +1,16 @@
> find-word@0.1.0 test
> vitest run
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 7 passed (7)
Tests 29 passed (29)
Start at 05:19:37
Duration 4.63s (tests 49%, environment 38%, setup 9%, transform 2%, import 1%, worker 1%)
Environment jsdom was created 7 times · 4.24s total, 38% of tracked time
create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
learn more: https://vitest.dev/guide/improving-performance#test-environments
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@@ -0,0 +1,29 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:754672) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 10 tests using 2 workers
(node:754693) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:754694) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:124:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (16.6s)
✓ 3 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.1s)
✓ 4 [desktop] › tests/browser/ui.spec.ts:50:1 › 난이도 설명은 모바일에서도 보인다 (756ms)
✓ 5 [desktop] › tests/browser/ui.spec.ts:70:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (11.8s)
(node:755063) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (58.8s)
(node:755272) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 7 [mobile] › tests/browser/game.spec.ts:124:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.2s)
✓ 8 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (939ms)
✓ 9 [mobile] › tests/browser/ui.spec.ts:50:1 › 난이도 설명은 모바일에서도 보인다 (711ms)
✓ 10 [mobile] › tests/browser/ui.spec.ts:70:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (11.9s)
✓ 6 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (56.3s)
10 passed (1.5m)
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+29
View File
@@ -0,0 +1,29 @@
> find-word@0.1.0 test:browser
> playwright test
[WebServer] (node:756541) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
[WebServer] (Use `node --trace-warnings ...` to show where the warning was created)
Running 10 tests using 2 workers
(node:756562) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
(node:756561) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 1 [desktop] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.0s)
✓ 3 [desktop] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (1.3s)
✓ 4 [desktop] › tests/browser/ui.spec.ts:54:1 › 난이도 설명은 모바일에서도 보인다 (1.0s)
✓ 5 [desktop] › tests/browser/ui.spec.ts:74:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (11.8s)
(node:756932) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 2 [desktop] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (57.0s)
(node:757164) Warning: The 'NO_COLOR' env is ignored due to the 'FORCE_COLOR' env being set.
(Use `node --trace-warnings ...` to show where the warning was created)
✓ 7 [mobile] › tests/browser/game.spec.ts:127:1 › 오디오 HTTP 실패를 화면에 표시하고 재시도한다 (14.2s)
✓ 8 [mobile] › tests/browser/ui.spec.ts:4:1 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 (881ms)
✓ 9 [mobile] › tests/browser/ui.spec.ts:54:1 › 난이도 설명은 모바일에서도 보인다 (792ms)
✓ 6 [mobile] › tests/browser/game.spec.ts:7:1 › 실제 오디오로 혼합 모드 완주·레이아웃·접근성·외부 요청 없음 (55.8s)
✓ 10 [mobile] › tests/browser/ui.spec.ts:74:1 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 (11.5s)
10 passed (1.4m)
+15
View File
@@ -0,0 +1,15 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html 0.89 kB │ gzip: 0.63 kB
dist/assets/index-KT6VDfPn.css 15.25 kB │ gzip: 4.39 kB
dist/assets/index-JDsKSYsp.js 244.43 kB │ gzip: 77.02 kB
✓ built in 302ms
빌드 음성 자산 101개 확인
+3
View File
@@ -0,0 +1,3 @@
> find-word@0.1.0 lint
> eslint . && tsc --noEmit
+16
View File
@@ -0,0 +1,16 @@
> find-word@0.1.0 test
> vitest run
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
Test Files 7 passed (7)
Tests 34 passed (34)
Start at 05:37:00
Duration 11.09s (tests 54%, environment 36%, setup 8%, import 1%, worker 1%, transform 1%)
Environment jsdom was created 7 times · 8.03s total, 36% of tracked time
create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
learn more: https://vitest.dev/guide/improving-performance#test-environments
@@ -0,0 +1,9 @@
> find-word@0.1.0 build
> tsc --noEmit && vite build && node scripts/check-build.mjs
tests/App.test.tsx(62,71): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
@@ -0,0 +1,9 @@
> find-word@0.1.0 lint
> eslint . && tsc --noEmit
tests/App.test.tsx(62,71): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Some files were not shown because too many files have changed in this diff Show More