feat: release Find-Word v0.1
@@ -0,0 +1,44 @@
|
||||
# ACCEPTANCE — Find-Word v0.1
|
||||
|
||||
## 제품
|
||||
|
||||
- [ ] H1 또는 브랜드 영역에 `단어를 맞춰라` 표시
|
||||
- [ ] `Designed by OZ at Heat-T Medical Inc.` 표시
|
||||
- [ ] v0.1 표시
|
||||
- [ ] 게임 시작부터 결과·재시작까지 완결된 흐름
|
||||
- [ ] 객관식과 주관식 모두 실제 플레이 가능
|
||||
- [ ] 최소 10개 문제 유형이 코드·데이터·테스트로 확인됨
|
||||
- [ ] 최소 8개 배경 환경 프리셋
|
||||
- [ ] 최소 5개 dB 기반 난이도
|
||||
- [ ] 문제 조합이 고정 10문제를 넘어 확장 가능
|
||||
- [ ] 사전 단어 기반 오답과 변형 규칙 metadata
|
||||
|
||||
## 오디오
|
||||
|
||||
- [ ] Web Audio API로 배경과 목표 음성 gain을 분리
|
||||
- [ ] 난이도별 상대 dB가 수학적으로 검증됨
|
||||
- [ ] 첫 재생은 사용자 gesture 이후 실행
|
||||
- [ ] 재생 중 중복 시작·다음 문제 전환 race 방지
|
||||
- [ ] 오디오 실패 UI와 재시도
|
||||
- [ ] provenance 문서와 생성 절차
|
||||
- [ ] 런타임 외부 TTS/API 호출 없음
|
||||
|
||||
## 품질
|
||||
|
||||
- [ ] strict TypeScript
|
||||
- [ ] 직접 작성한 소스에 상세한 한국어 주석
|
||||
- [ ] unit/component 테스트에서 문제 유형 수, 선택지 무결성, 정답 판정, 주관식 정규화, 난이도 dB, 점수, 오디오 상태 검증
|
||||
- [ ] lint 통과
|
||||
- [ ] test 통과
|
||||
- [ ] production build 통과
|
||||
- [ ] 접근성 이름, 키보드 focus, reduced motion
|
||||
- [ ] 360px 모바일과 1280px 데스크톱 레이아웃
|
||||
- [ ] secret scan 무검출
|
||||
|
||||
## 문서
|
||||
|
||||
- [ ] README에 개발·테스트·빌드 방법
|
||||
- [ ] ARCHITECTURE에 데이터·상태·오디오 설계
|
||||
- [ ] AUDIO_PROVENANCE에 생성·라이선스·재생 방식
|
||||
- [ ] DEPLOYMENT에 Nginx static 배포와 SPA fallback
|
||||
- [ ] `.agent/RESULT.md`에 실제 RED/GREEN 및 최종 검증 기록
|
||||
@@ -0,0 +1,31 @@
|
||||
# CONTEXT — Find-Word v0.1
|
||||
|
||||
## 저장소와 책임 경계
|
||||
|
||||
- canonical repository: `https://source.ibit.kr/OZ-Group/Find-Word`
|
||||
- 작업 브랜치: `feat/v0.1`
|
||||
- 현재 worktree: `/home/hermes/worktrees/find-word-v0.1`
|
||||
- Codex는 이 worktree만 수정한다.
|
||||
- Neo가 최종 diff 검토, 테스트, source push, Heart 서버 배포, Nginx/TLS/DNS 검증을 담당한다.
|
||||
|
||||
## 배포 예정 환경
|
||||
|
||||
- Ubuntu 24.04
|
||||
- Nginx 1.24
|
||||
- Node.js 22는 설치되어 있으나 프로덕션은 정적 `dist/`를 Nginx가 직접 제공하는 구성을 선호한다.
|
||||
- 예정 도메인 `findword.heart-m.com`은 현재 Heart 서버 IP로 DNS 해석된다.
|
||||
- 배포 파일·서비스·포트는 아직 존재하지 않는다.
|
||||
|
||||
## 제품 해석
|
||||
|
||||
- “4개의 단어”를 우선한다. 정답 1개와 변형 규칙이 서로 다른 사전 단어 오답 3개를 표시한다.
|
||||
- 사용자 지정 표기를 해석하거나 교정하지 않는다. UI·README·문서·테스트에는 정확히 `Designed by OZ at Heat-T Medical Inc.`를 사용한다.
|
||||
- 전체 판매/의료 판단 서비스가 아니라 청각적 주의·식별 게임이다. 의료 효과·치료·진단을 주장하지 않는다.
|
||||
- 오디오 난이도는 디지털 gain의 상대 dB로 계산하고 UI에 난이도와 상대 dB를 표시한다.
|
||||
|
||||
## 보안·라이선스
|
||||
|
||||
- 외부 CDN·분석·런타임 API 의존을 두지 않는다.
|
||||
- 외부 음원을 다운로드할 경우 명시적 라이선스와 원본 URL이 필요하다. 불확실하면 직접 합성한다.
|
||||
- 앱은 사용자 입력을 HTML로 삽입하지 않는다.
|
||||
- 결과는 localStorage에 선택적으로 저장할 수 있으나 개인정보는 저장하지 않는다.
|
||||
@@ -0,0 +1,410 @@
|
||||
# Find-Word v0.1 작업 결과
|
||||
|
||||
작성일: 2026-10-14 UTC. 작업 위치: `/home/hermes/worktrees/find-word-v0.1`.
|
||||
입력 문서 `.agent/CONTEXT.md`, `TASK.md`, `ACCEPTANCE.md`를 모두 읽고 적용했다.
|
||||
기존 입력 문서는 수정하지 않았다.
|
||||
|
||||
## 구현 결과
|
||||
|
||||
- `단어를 맞춰라`, `Designed by OZ at Heat-T Medical Inc.`, `v0.1` 표시.
|
||||
- 반응형 시작·게임·결과 화면, 10문제 완주, 재시작, 다시 듣기, 중단, 진행률.
|
||||
- 객관식·주관식·혼합 모드, 10유형 × 10단어 = 100개 저장 문장.
|
||||
- 4지선다의 고유한 정답 1개·사전 단어 오답 3개, 변형 규칙과 예상 혼동 난이도.
|
||||
- 8개 합성 배경 환경, +6/+3/0/−3/−6 dB의 5단계 상대 음량.
|
||||
- 사용자 gesture에서 AudioContext 시작, 음성·배경 gain 분리, 안내 fade,
|
||||
재생 중 경합 방지, 취소된 로드의 늦은 완료 무시, 오류와 재시도.
|
||||
- 점수·정답률·연속 정답·단계별 결과, 한글 NFC/공백/문장부호 정규화,
|
||||
한글 조합 중 Enter·재시작·재청취의 입력 상태 보호.
|
||||
- 키보드/터치, 질문·피드백·결과 focus, ARIA, reduced motion, 색 대비.
|
||||
- TypeScript strict, 직접 작성 소스 22개 파일의 한국어 설계 설명.
|
||||
- 직접 합성 WAV 101개(10,864,582바이트), 결정론적 배경 합성,
|
||||
생성 도구 실측 버전·원문·파일 해시 manifest.
|
||||
- 런타임 외부 CDN/TTS/API/분석 요청 없음. 개인정보 저장·로그인·추적 없음.
|
||||
|
||||
서버·DNS·인증정보에 접근하지 않았으며 **배포, Git commit, Git push를 실행하지 않았다.**
|
||||
Playwright의 localhost preview는 개발 검증용으로만 실행했다.
|
||||
|
||||
## 실제 strict TDD 기록
|
||||
|
||||
먼저 테스트와 타입 계약/비동작 stub을 작성하고 RED 명령의 종료 코드 **1**을
|
||||
확인한 뒤 최소 동작을 구현했다. 각 GREEN은 종료 코드 **0**이다. 아래 원문
|
||||
로그는 `evidence/`에 보관했다. 테스트를 건너뛰거나 실패 조건을 지워 GREEN으로
|
||||
만들지 않았다. 초기 stub의 throw는 구현 전 상태를 표현한다.
|
||||
|
||||
| 순서·핵심 동작 | 실제 RED 명령·결과 | 최소 구현 뒤 GREEN 명령·결과 | 원문 로그 |
|
||||
| --- | --- | --- | --- |
|
||||
| 01 데이터: 10유형·단어 무결성·8환경·5단계 | `npm test -- tests/content.test.ts`: **3 failed**, 빈 목록의 개수/환경 불일치 | 같은 명령: **3 passed** | [RED](evidence/01-content-red.log), [GREEN](evidence/01-content-green.log) |
|
||||
| 02 출제·판정·게임 전이·점수 | `npm test -- tests/game.test.ts`: **6 failed**, 출제 미구현·정규화 불일치 | 같은 명령: **6 passed** | [RED](evidence/02-game-red.log), [GREEN](evidence/02-game-green.log) |
|
||||
| 03 dB·RMS·결정론적 환경 합성 | `npm test -- tests/dsp.test.ts`: **3 failed**, gain=1·잘못된 RMS·무음 | 같은 명령: **3 passed** | [RED](evidence/03-dsp-red.log), [GREEN](evidence/03-dsp-green.log) |
|
||||
| 04 오디오 gesture·gain·fade·중복 시작·취소·실패·음량 | `npm test -- tests/audio.test.ts`: **5 failed**, 오디오 미구현 | 같은 명령: **5 passed** | [RED](evidence/04-audio-red.log), [GREEN](evidence/04-audio-green.log) |
|
||||
| 05 실제 저장 음성·원문·해시·포맷·크기 | `npm test -- tests/assets.test.ts`: **1 failed**, manifest 없음(ENOENT) | `npm run audio:generate` 후 `npm test -- tests/App.test.tsx tests/assets.test.ts`: **2 files / 7 passed** (음성 1 + UI 6) | [RED](evidence/05-assets-red.log), [GREEN](evidence/06-ui-05-assets-green.log) |
|
||||
| 06 시작→재생→답변→결과→재시작·오류·포커스·보안 출력 | `npm test -- tests/App.test.tsx`: **6 failed**, 빈 화면에서 브랜드/시작/입력 역할 없음 | 위 결합 명령: **7 passed** 중 UI **6 passed** | [RED](evidence/06-ui-red.log), [GREEN](evidence/06-ui-05-assets-green.log) |
|
||||
| 07 production 음성 검증기 | `npm test -- tests/build.test.ts`: **1 failed**, 자산 수 0 ≠ 101 | 같은 명령: **1 passed**, 정상 통과 및 파일 변조·누락 시 실제 실패 검증 | [RED](evidence/07-build-red.log), [GREEN](evidence/07-build-green.log) |
|
||||
| 08 오답 변형별 혼동 난이도 | `npm test -- tests/content.test.ts`: **1 failed / 3 passed**, 순위 undefined | 같은 명령: **4 passed** | [RED](evidence/08-rule-red.log), [GREEN](evidence/08-rule-green.log) |
|
||||
| 09 한글 조합 중 제출 방지 | `npm test -- tests/App.test.tsx`: **1 failed / 6 passed**, 조합 중 정답 피드백이 나타남 | 같은 명령: **7 passed** | [RED](evidence/09-ime-red.log), [GREEN](evidence/09-ime-green.log) |
|
||||
| 10 한글 조합 도중 세션 재시작 | `npm test -- tests/App.test.tsx -t '한글 조합 도중'`: **1 failed / 7 skipped**, 새 세션 제출이 막힘 | 같은 명령: **1 passed / 7 skipped** | [RED](evidence/10-ime-reset-red.log), [GREEN](evidence/10-ime-reset-green.log) |
|
||||
| 11 실측 음성 도구 버전 provenance | `npm test -- tests/assets.test.ts -t '도구 버전'`: **1 failed / 1 skipped**, 버전 정보 undefined | 생성기 수정·재생성 후 `npm test -- tests/assets.test.ts`: **2 passed** | [RED](evidence/11-provenance-red.log), [GREEN](evidence/11-provenance-green.log) |
|
||||
| 12 한글 조합 도중 다시 듣기 | 위 10의 테스트에 재청취 전환을 추가한 동일 명령: **1 failed / 7 skipped**, 제출이 막힘 | 같은 명령: **1 passed / 7 skipped** | [RED](evidence/12-ime-replay-red.log), [GREEN](evidence/12-ime-replay-green.log) |
|
||||
|
||||
`-t`의 skipped는 해당 회귀만 고른 실행 결과이며 최종 전체 실행에는 **skip 없음**.
|
||||
|
||||
중간 실패도 남겼다.
|
||||
|
||||
- DSP의 첫 구현에서 음수 0(`-0`)이 경계 샘플로 남아 **1 failed / 2 passed**였다.
|
||||
경계값을 명시적으로 0으로 써 수정했다. [중간 로그](evidence/03-dsp-intermediate.log)
|
||||
- UI 첫 구현에서 라디오의 보조 설명에 ‘직접 입력’이 중복되어 **1 failed / 6 passed**였다.
|
||||
각 라디오의 접근성 이름을 명확히 했다. [중간 로그](evidence/06-ui-intermediate.log)
|
||||
- 오디오 최초 RED의 미구현 throw로 미처리 rejection 3개도 보고되었다.
|
||||
실제 엔진의 Promise 종료·정리 구현 뒤 GREEN과 최종 실행에서는 0개다.
|
||||
- 첫 TypeScript 검사에서 TypedArray의 ArrayBuffer 타입과 테스트 mock의 순환
|
||||
추론 오류를 발견했다. 반환 타입·mock 타입을 명시하여 strict 검사를 통과시켰다.
|
||||
|
||||
## 브라우저 통합 검증과 접근성 수정
|
||||
|
||||
실제 Chrome, production `dist/`, 실제 WAV/Web Audio로 검증했다. 모의 재생으로
|
||||
시간을 단축하지 않고 `ended`까지 기다렸다. 모든 접근성 검사는 axe 규칙을
|
||||
비활성화하지 않고 실행했다.
|
||||
|
||||
명령: `CHROME_PATH=/usr/bin/google-chrome npm run test:browser`
|
||||
|
||||
| 실행 | 실제 결과 | 발견·조치 |
|
||||
| --- | --- | --- |
|
||||
| [첫 실행](evidence/browser-first.log) | 2 passed / 2 failed | 시작 화면 글자 대비 3.75, 4.07 < 4.5. 두 색을 어둡게 조정 |
|
||||
| [두 번째](evidence/browser-second.log) | 2 passed / 2 failed | 게임 화면 글자 대비 4.08, 4.41 < 4.5. 두 색을 조정 |
|
||||
| [세 번째](evidence/browser-third.log) | 2 passed / 2 failed | 두 크기 모두 10문제 완주·100% 도달. 결과 설명 대비 4.38 < 4.5 수정 |
|
||||
| [수정 후](evidence/browser-final.log) | **4 passed (1.2m)** | 시작·게임·결과 axe 위반 0, 두 크기 완주·재시작·404 후 재시도 통과 |
|
||||
|
||||
최종 빌드에서도 **4 passed (1.2m), 종료 코드 0**을 확인했다. reduced motion의
|
||||
실제 계산된 `animation-name: none`까지 검증했다.
|
||||
[최종 브라우저 로그](evidence/final-browser.log)에 실제 결과를 보관했다.
|
||||
|
||||
스크린샷:
|
||||
|
||||
- [1280px 시작](evidence/desktop-home.png), [게임](evidence/desktop-game.png), [결과](evidence/desktop-results.png)
|
||||
- [360px 시작](evidence/mobile-home.png), [게임](evidence/mobile-game.png), [결과](evidence/mobile-results.png)
|
||||
|
||||
## 최종 검증
|
||||
|
||||
환경: Node.js **26.7.0**, npm **11.19.0**, Vite **8.3.4**, Vitest **5.0.4**.
|
||||
최소 Node 안내는 아래 릴리스 재검토에서 **22.22.2 이상**으로 바로잡았다.
|
||||
실제 Node 22에서의 별도 실행은 하지 않았으며 현재 실행 환경은 아래 최신 검증을 따른다.
|
||||
|
||||
| 명령/검증 | 실제 결과 |
|
||||
| --- | --- |
|
||||
| `npm run lint` | **PASS**, ESLint + `tsc --noEmit` 오류 0 · [로그](evidence/final-lint.log) |
|
||||
| `npm test` | **7 files / 29 tests passed**, skip 0 · [로그](evidence/final-test.log) |
|
||||
| `npm run build` | **PASS**, `dist/` 생성 및 음성 101개 원본 SHA-256 대조 · [로그](evidence/final-build.log) |
|
||||
| `CHROME_PATH=/usr/bin/google-chrome npm run test:browser` | **4 passed (1.2m)**, 360px/1280px 완주·오류 복구·접근성 위반 0·외부 요청 0·JS 예외 0 · [로그](evidence/final-browser.log) |
|
||||
| `npm run audio:generate` 후 이전 manifest `files` 비교 | **101개 원문·바이트 수·SHA-256 전부 동일** · [로그](evidence/audio-reproduce.log) |
|
||||
| `npm audit --audit-level=high` | **found 0 vulnerabilities** · [로그](evidence/final-audit.log) |
|
||||
| 비밀값 정적 패턴 검사 | **34개 텍스트 파일, 0 matches** · [로그](evidence/final-security.log) |
|
||||
| 직접 작성 소스의 한글 설명 | **22개 파일, 누락 0**; Python 생성기 AST 파싱 PASS |
|
||||
| `git diff --check` | 종료 0. 초기 저장소가 비어 새 파일들은 아직 untracked 상태 |
|
||||
|
||||
production 주요 번들: JS **244.26 kB**(gzip 76.95 kB), CSS **14.87 kB**(gzip 4.29 kB),
|
||||
HTML **0.89 kB**. WAV는 별도로 10.86 MB이고 문항 단위로 로드한다.
|
||||
|
||||
비밀값 검사는 소스·테스트·스크립트·문서·루트 metadata·입력 `.agent/*.md`에 대해
|
||||
AWS access-key 형식, GitHub/OpenAI 토큰 형식, private-key 블록, 긴 password/secret/API-key
|
||||
문자열 할당을 Python 정규식으로 검사했다. 일치 값은 출력하지 않는다.
|
||||
의존성 디렉터리와 PCM 바이너리는 대상에서 제외했다. 이는 특정 형식의 정적 검사다.
|
||||
|
||||
## 변경 파일
|
||||
|
||||
초기 저장소에는 입력 문서만 존재했다. 다음 파일을 새로 작성했다. 파일 전체
|
||||
목록은 [changed-files.txt](evidence/changed-files.txt)에 남긴다(입력 문서는 보존 표시).
|
||||
|
||||
- 앱: `src/App.tsx`, `src/main.tsx`, `src/styles.css`, `src/content.ts`, `src/game.ts`,
|
||||
`src/audio/dsp.ts`, `src/audio/engine.ts`, `index.html`.
|
||||
- 설정: `package.json`, `package-lock.json`, `tsconfig.json`, `vite.config.ts`,
|
||||
`eslint.config.js`, `playwright.config.ts`, `.gitignore`.
|
||||
- 생성/검증: `scripts/generate_audio.py`, `scripts/check-build.mjs`.
|
||||
- 테스트: `tests/setup.ts`, `tests/content.test.ts`, `tests/game.test.ts`,
|
||||
`tests/dsp.test.ts`, `tests/audio.test.ts`, `tests/assets.test.ts`,
|
||||
`tests/App.test.tsx`, `tests/build.test.ts`, `tests/browser/game.spec.ts`.
|
||||
- 자산: `public/audio/intro.wav`, 100개의 `<pattern>-<word>.wav`,
|
||||
`public/audio/manifest.json`.
|
||||
- 문서: `README.md`, `docs/ARCHITECTURE.md`, `docs/AUDIO_PROVENANCE.md`,
|
||||
`docs/DEPLOYMENT.md`, `docs/WORDS.md`, `.agent/RESULT.md` 및 `evidence/` 로그·화면.
|
||||
- 생성된 `dist/`, `node_modules/`, `.cache/`와 Playwright 임시 보고서는 Git 제외.
|
||||
|
||||
## 남은 위험·인수 사항
|
||||
|
||||
1. 한국어 포먼트 합성은 자연스러운 사람의 발화보다 기계적이다. 자동 검증은
|
||||
오디오의 존재·디코딩·전력·흐름을 확인했고, 실제 사용자의 발음 이해도와 청감
|
||||
난이도를 평가하지 않았다. Neo의 직접 청취 및 사용자 평가가 필요하다.
|
||||
2. 환경음은 장소를 추상화한 합성 잡음이다. 실제 시장·카페의 사람 대화 녹음이
|
||||
아니다. 상대 dB와 오답 혼동 순위는 의료·청력 측정치가 아니다.
|
||||
3. 모바일은 Chrome의 360px 터치 에뮬레이션으로 검증했다. 실물 iOS Safari,
|
||||
Android 장치, Bluetooth 출력, 보조공학 장치의 전체 조합은 미검증이다.
|
||||
4. 사용자 기록은 메모리에만 있어 새로고침 시 사라진다. 오프라인 PWA는 아니며
|
||||
음성의 첫 로드에는 정적 호스트 연결이 필요하다. 전체 오디오 용량은 약 10.9MB다.
|
||||
5. 정답은 정적 번들에 포함된다. 개발자 도구로 답을 알아낼 수 있어 보안 시험이나
|
||||
순위 경쟁 서비스 용도가 아니다.
|
||||
6. 서버 배포·Nginx/TLS/DNS는 요청 범위 밖이며 실행하지 않았다. Neo가 별도
|
||||
검토·커밋·배포하고 실제 도메인에서 MIME·캐시·오디오 404·HTTPS를 확인해야 한다.
|
||||
|
||||
## Neo 스크린샷 검수 후 출시 전 UI 수정 — 2026-10-14 UTC
|
||||
|
||||
앞의 기록과 기존 스크린샷은 보존하고 이번 작업의 RED/GREEN을 이어서 기록한다.
|
||||
기록 위치는 `.agent/TASK.md`와 `.agent/ACCEPTANCE.md`의 `.agent/RESULT.md` 지정을 따랐다.
|
||||
|
||||
### 변경 결과
|
||||
|
||||
- 한국어 `h1`·`h2`·`h3`에 `word-break: keep-all`을 적용했다. 실제 Chrome의
|
||||
360px computed style이 `normal`인 RED부터 확인했고, 열 가지 질문 모두
|
||||
360px/1280px에서 `keep-all`과 제목 내부 가로 넘침 여부를 검사한다.
|
||||
- 게임과 결과 난이도를 `SNR +6 dB`, `SNR +3 dB`, `SNR 0 dB`, `SNR -3 dB`,
|
||||
`SNR -6 dB`로 표시한다. 게임에서는 “음성이 배경보다 6 dB 커요.”,
|
||||
“음성과 배경의 크기가 같아요.”, “음성이 배경보다 3 dB 작아요.”처럼
|
||||
크기 관계를 풀어 설명한다. 모바일에서도 설명을 숨기지 않는다.
|
||||
- 전체 음량은 시작 화면의 설정 카드 안에서 CTA보다 앞에, 게임에서는 환경
|
||||
패널 안에 배치했다. `전체 음량` 접근성 이름, label 연결, 엔진 전달,
|
||||
화면 전환 시 값 유지와 결과 화면의 기존 음량 조절을 보존했다.
|
||||
- 사운드 룸의 `01 / 08`은 실제 환경 수를 이용한 `8가지 환경`으로 바꿨다.
|
||||
`플레이 방법`의 `#howto` 앵커는 유지하고 장식 화살표를 `↓`로 바꿨다.
|
||||
CTA의 표시와 접근성 이름은 `게임 시작 · 소리 듣기`로 통일했다.
|
||||
- 기존 색상·캐릭터·게임 흐름을 유지했다. README의 CTA와 SNR 설명도 맞췄다.
|
||||
출제·채점·음원·오디오 gain 구현은 수정하지 않았다.
|
||||
|
||||
### 추가 strict TDD 기록
|
||||
|
||||
각 RED는 해당 구현을 바꾸기 **전에** 실행해 종료 코드 **1**을 확인했다.
|
||||
각 GREEN은 같은 테스트 계약으로 종료 코드 **0**을 확인했다. 브라우저는
|
||||
각 단계의 소스를 먼저 빌드한 뒤 localhost production preview로 검사했다.
|
||||
|
||||
| 순서·계약 | 실제 RED | 구현 후 GREEN | 증거 |
|
||||
| --- | --- | --- | --- |
|
||||
| 13 한국어 제목의 어절 보존 | `CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/ui.spec.ts --project=mobile`: **1 failed**. 시작·질문 heading의 computed `wordBreak`가 `normal` | 같은 명령: **1 passed**, `keep-all` 및 가로 넘침 없음 | [RED](evidence/13-heading-red.log), [GREEN](evidence/13-heading-green.log), [변경 전 빌드](evidence/13-heading-build-before.log), [변경 후 빌드](evidence/13-heading-build-after.log) |
|
||||
| 14 다섯 SNR·크기 설명·결과 표시 | `npm test -- tests/App.test.tsx -t '다섯 난이도의 SNR'`: **1 failed / 8 skipped**. 열 문제의 SNR·크기 설명과 결과 표기 누락 | 같은 명령: **1 passed / 8 skipped** | [RED](evidence/14-snr-red.log), [GREEN](evidence/14-snr-green.log) |
|
||||
| 14 모바일 난이도 설명 가시성 | `CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/ui.spec.ts --project=mobile --grep '난이도 설명'`: **1 failed**. `.db-note`가 hidden이고 새 설명·배지 없음 | 같은 명령: **1 passed** | [RED](evidence/14-snr-browser-red.log), [GREEN](evidence/14-snr-browser-green.log), [빌드](evidence/14-snr-build.log) |
|
||||
| 15 음량의 소속·문서 순서·이름·값·엔진 | `npm test -- tests/App.test.tsx -t '전체 음량을 시작 CTA'`: **1 failed / 9 skipped**. 설정 카드·환경 패널 바깥이고 CTA 뒤에 있음 | 같은 명령: **1 passed / 9 skipped**. 이름·단일 컨트롤·20→60% 전환 및 엔진 전달 확인 | [RED](evidence/15-volume-red.log), [GREEN](evidence/15-volume-green.log) |
|
||||
| 15 실제 음량 위치·키보드 | `CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/ui.spec.ts --grep '전체 음량'`: **2 failed**. 두 화면 크기에서 컨트롤이 카드 경계와 CTA 아래로 벗어남 | 같은 명령: **2 passed**, 카드 내부·CTA 이전 배치와 방향키·값 유지 확인 | [RED](evidence/15-volume-browser-red.log), [GREEN](evidence/15-volume-browser-green.log), [빌드](evidence/15-volume-build.log) |
|
||||
| 16 환경 수·앵커 화살표·명확한 CTA | `npm test -- tests/App.test.tsx -t '시작 안내'`: **3 failed / 10 skipped**. `8가지 환경`·`↓`·새 CTA 없음 | 같은 명령: **3 passed / 10 skipped**, CTA 클릭에서 재생 1회 확인 | [RED](evidence/16-copy-red.log), [GREEN](evidence/16-copy-green.log) |
|
||||
| 17 음량 이동 후 게임 카드·바닥글 여백 | `CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/ui.spec.ts --grep '전체 음량'`: **2 failed**. 스크린샷 검토에서 발견한 두 화면 크기의 카드·바닥글 간격 **0px < 24px** | 게임 하단에 32px 여백 적용 후 같은 명령: **2 passed** | [RED](evidence/17-spacing-red.log), [GREEN](evidence/17-spacing-green.log) |
|
||||
|
||||
필터 실행의 skipped는 해당 회귀만 선택한 결과다. 기존 테스트의 시작 버튼
|
||||
선택자도 새 접근성 이름에 맞췄으며, 이전 이름까지 허용하는 정규식으로
|
||||
검증을 완화하지 않았다. 최종 브라우저 테스트에는 음량에서 Tab을 누르면
|
||||
시작 CTA로 이동하는 검사와 열 가지 질문의 computed style 검사도 포함한다.
|
||||
|
||||
전체 검증의 첫 타입 검사에서 새 Testing Library `getByRole` 호출에 지원되지
|
||||
않는 `exact` 옵션을 발견했다. 문자열 `name` 자체가 정확 일치를 하므로 불필요한
|
||||
옵션만 제거했다. [중간 lint](evidence/neo-intermediate-lint.log)와
|
||||
[중간 build](evidence/neo-intermediate-build.log)를 보존했다.
|
||||
|
||||
여백 회귀는 첫 전체 브라우저 실행 **10 passed (1.5m)** 뒤 스크린샷 검토에서
|
||||
추가로 발견했다. [당시 로그](evidence/neo-before-spacing-browser.log)를 보존하고
|
||||
17의 측정 계약을 먼저 실패시킨 뒤 수정했다. 전체 캡처 전에는 스크롤을
|
||||
맨 위로 맞춰 화면 밖 fixed 건너뛰기 링크가 Chrome의 full-page 이미지에
|
||||
들어오는 캡처 아티팩트를 방지했다. 앱의 초점 처리와 링크는 바꾸지 않았다.
|
||||
|
||||
### 이번 수정의 최종 전체 검증
|
||||
|
||||
17의 여백 수정까지 포함한 최종 소스로 아래 명령을 모두 실행했다. 종료 코드는
|
||||
전부 **0**이며 unit/component와 브라우저 전체 실행 모두 **skip 없음**이다.
|
||||
|
||||
| 명령 | 최종 결과 | 로그 |
|
||||
| --- | --- | --- |
|
||||
| `npm run lint` | **PASS**, ESLint와 TypeScript 오류 0 | [lint](evidence/neo-final-lint.log) |
|
||||
| `npm test` | **7 files / 34 tests passed** | [test](evidence/neo-final-test.log) |
|
||||
| `npm run build` | **PASS**, production 빌드·음성 101개 무결성 확인 | [build](evidence/neo-final-build.log) |
|
||||
| `CHROME_PATH=/usr/bin/google-chrome npm run test:browser` | **10 passed (1.4m)**, 실제 Chrome 360px/1280px | [browser](evidence/neo-final-browser.log) |
|
||||
|
||||
브라우저에서는 실제 WAV 재생을 기다려 혼합 모드 열 문제를 완주하고,
|
||||
재청취·100% 결과·재시작·404 후 재시도·초점 복귀를 검증했다. 두 화면 크기에서
|
||||
시작·게임·결과 axe 위반 **0**, 외부 요청 **0**, JavaScript 예외 **0**이며,
|
||||
reduced motion과 새 줄바꿈·SNR 설명·음량 위치·키보드 계약도 모두 통과했다.
|
||||
스크린샷을 직접 열어 시작 CTA 앞 음량, 게임 환경 패널의 SNR·음량, 어절을
|
||||
보존한 질문, 게임 카드와 바닥글의 여백, 결과 표기를 확인했다.
|
||||
|
||||
최종 스크린샷(기존 파일과 구별되는 `neo-` 접두사):
|
||||
|
||||
- 1280px: [시작](evidence/neo-desktop-home.png), [게임](evidence/neo-desktop-game.png), [결과](evidence/neo-desktop-results.png), [고정 질문](evidence/neo-desktop-heading.png)
|
||||
- 360px: [시작](evidence/neo-mobile-home.png), [게임](evidence/neo-mobile-game.png), [결과](evidence/neo-mobile-results.png), [고정 질문](evidence/neo-mobile-heading.png)
|
||||
|
||||
이번 변경 파일은 `src/App.tsx`, `src/styles.css`, `tests/App.test.tsx`,
|
||||
`tests/browser/game.spec.ts`, 신규 `tests/browser/ui.spec.ts`, `README.md`,
|
||||
이 `.agent/RESULT.md` 및 추가 evidence 로그·스크린샷이다. 의존성과 음원은
|
||||
변경하지 않았다. `git diff --check`는 종료 0이며 저장소의 기존 untracked 상태는
|
||||
유지했다. 변경 텍스트 7개 파일의 후행 공백 0과 RESULT의 evidence 링크 존재도
|
||||
별도로 확인했다.
|
||||
|
||||
요청된 UI 수정에서 남은 실패는 없다. 실물 모바일·Safari 및 청감 평가의 기존
|
||||
검증 범위 한계는 그대로다. 이번에도 로컬 preview 검증만 수행했으며,
|
||||
**서버 배포, Git commit, Git push는 실행하지 않았다.**
|
||||
|
||||
|
||||
## 독립 릴리스 리뷰·Neo literal-preservation 재검토 반영 — 2026-10-14 UTC
|
||||
|
||||
이번 사용자 지시를 이전 입력 문서의 브랜드 해석보다 우선 적용했다.
|
||||
정확한 표기는 **Designed by OZ at Heat-T Medical Inc.**다. UI, README,
|
||||
`.agent/CONTEXT.md`·`TASK.md`·`ACCEPTANCE.md`의 요구사항과 이 문서의 표기도
|
||||
일치시켰다. 이전 실행의 raw 로그와 스크린샷은 당시 상태의 증거이므로 보존한다.
|
||||
아래 검증이 현재 릴리스 상태이며 앞의 결과·바이트 수·테스트 수는 당시 기록이다.
|
||||
|
||||
### 변경 내용
|
||||
|
||||
- 브랜드 원문을 고정한 컴포넌트·문서·실제 브라우저 회귀 검증을 추가했다.
|
||||
회사명을 문맥으로 추측해 교정하던 입력 문서 규칙도 제거했다.
|
||||
- 프로젝트 자체는 `private: true`, `license: UNLICENSED`, root `LICENSE`의
|
||||
private / all-rights-reserved로 명시했다. package repository·homepage와
|
||||
README·배포 문서의 canonical URL은 `https://source.ibit.kr/OZ-Group/Find-Word`다.
|
||||
- root와 public의 `THIRD_PARTY_NOTICES.md`에 잠긴 React 19.3.0, ReactDOM 19.3.0,
|
||||
scheduler 0.28.0의 MIT 전문을 포함했다. 빌드 검증기는 전이 의존성을 포함한
|
||||
런타임 목록을 lockfile에서 찾고, 설치된 LICENSE 전문·버전과 root/public/dist의
|
||||
고지 사본을 대조한다. notice의 누락·축약을 실제 실패시키는 테스트도 추가했다.
|
||||
- `Pattern.mechanic`에 10개 고유 연산을 지정했다. 직접 목표 식별, 암호 구별,
|
||||
잊고 기억하기, 첫째·마지막·가운데, 반복, 정정, 신호 조건, 오른쪽 표지를 각각
|
||||
다르게 듣고 판단하게 했다. `renderSentence`가 실제 보기의 오답도 문장에
|
||||
삽입하며 UI와 음성 생성기가 같은 원문을 사용한다. 문장 계약 검증은 100개
|
||||
조합에서 순서·반복 횟수·취소·조건·방향 단서와 질문 의도를 확인한다.
|
||||
- 101개 WAV와 manifest를 재생성했다. 합계 **20,497,222바이트**, 16kHz mono
|
||||
PCM이며 기존 20MiB 제한을 유지했다. 방향은 말로 전달하는 의미 단서다.
|
||||
- package와 lockfile의 Node 최소 버전은 `>=22.22.2`로 맞췄다. README·배포
|
||||
문서에 잠긴 jsdom 계열의 24.x 최소 24.15.0 요구도 함께 설명했다.
|
||||
- Playwright는 `chromiumSandbox: true`가 기본이다. 미설정·빈 값·`0`·`false`는
|
||||
sandbox를 유지하며, `PLAYWRIGHT_NO_SANDBOX=1`일 때만 끈다. Chrome 경로
|
||||
지정은 sandbox와 독립적이다. 새 브라우저 스크린샷은 `release-` 접두사로 저장한다.
|
||||
|
||||
### 실제 strict TDD
|
||||
|
||||
아래 RED는 구현을 바꾸기 전에 실제 실행했고 모두 종료 코드 **1**이었다.
|
||||
GREEN은 같은 계약으로 모두 종료 코드 **0**이었다. 브라우저 전달 계약은
|
||||
기존 dist에서 먼저 실패시킨 후 새 소스를 빌드했다. 테스트 실패를 제거하거나
|
||||
허용치를 완화하지 않았다. `-t`로 지정한 실행의 skip은 선택 실행에 한정된다.
|
||||
|
||||
| 순서·계약 | RED 명령·관찰 | GREEN 명령·관찰 | 원문 로그 |
|
||||
| --- | --- | --- | --- |
|
||||
| 18 literal 보존 | `npm test -- tests/App.test.tsx tests/release.test.ts -t '브랜드'`: **2 failed**, UI·문서의 기존 표기 불일치 | 같은 명령: **2 passed / 12 skipped** | [RED](evidence/18-literal-red.log), [GREEN](evidence/18-literal-green.log) |
|
||||
| 19 프로젝트 metadata·MIT 고지·배포 검증기 | `npm test -- tests/release.test.ts tests/build.test.ts`: **4 failed / 2 passed**, metadata·LICENSE·고지 누락 및 검증기의 누락/축약 미검출 | 같은 명령: **6 passed** | [RED](evidence/19-notices-red.log), [GREEN](evidence/19-notices-green.log) |
|
||||
| 20 고유 청취 연산·문장 계약·출제 | `npm test -- tests/content.test.ts tests/game.test.ts`: **12 failed / 10 passed**, mechanic 없음·경쟁 단어/순서/의미 계약 미충족 | 같은 명령: **22 passed** | [RED](evidence/20-mechanics-red.log), [GREEN](evidence/20-mechanics-green.log) |
|
||||
| 20 변경 원문과 WAV 대응 | `npm test -- tests/assets.test.ts`: **1 failed / 1 passed**, 기존 manifest 원문이 새 문장과 다름 | `npm run audio:generate` 후 `npm test -- tests/content.test.ts tests/game.test.ts tests/assets.test.ts`: **24 passed**, 문장·해시·PCM·비무음·크기·피크 검증 | [RED](evidence/20-audio-red.log), [생성](evidence/20-audio-generate.log), [GREEN](evidence/20-audio-green.log) |
|
||||
| 21 Node 최소 버전 | `npm test -- tests/release.test.ts -t 'Node 최소'`: **1 failed / 3 skipped**, package/lock/docs 값이 잠금 의존성 요구와 다름 | 같은 명령: **1 passed / 3 skipped** | [RED](evidence/21-node-red.log), [GREEN](evidence/21-node-green.log) |
|
||||
| 22 sandbox 기본값과 명시적 opt-in | `npm test -- tests/playwright-config.test.ts`: **5 failed**, 기본 sandbox 비활성화·무조건 적용된 비활성화 플래그 | 같은 명령: **5 passed**, 각 환경변수 값을 별도 프로세스로 검증 | [RED](evidence/22-sandbox-red.log), [GREEN](evidence/22-sandbox-green.log) |
|
||||
| 23 실제 HTTP 배포 응답 | `env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome npm run test:browser -- tests/browser/release.spec.ts --project=mobile`: **1 failed**, 이전 dist의 크레딧 불일치와 notice 대신 SPA HTML 응답 | 새 build 후 전체 브라우저 실행: **12 passed (2.2m)**, 두 화면 크기의 전달 계약 포함 | [RED](evidence/23-delivery-browser-red.log), [전체 GREEN](evidence/release-final-browser.log) |
|
||||
|
||||
중간 lint에서 새 sandbox 테스트의 env 객체 타입이 좁게 추론되어 TypeScript
|
||||
오류 2개가 났다. `NodeJS.ProcessEnv`를 명시하여 수정했다.
|
||||
[실패 원문](evidence/release-intermediate-lint.log)을 보존했다.
|
||||
|
||||
|
||||
### 최종 검증 결과
|
||||
|
||||
환경: Node.js **26.7.0**, npm **11.19.0**, 시스템 Chrome
|
||||
`/usr/bin/google-chrome`. 최소 Node 22.22.2에서 별도로 실행한 결과는 아니다.
|
||||
새 소스의 production build를 완료한 뒤 다음 명령으로 브라우저 전체를 실행했다.
|
||||
|
||||
```bash
|
||||
env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome DEBUG=pw:browser npm run test:browser
|
||||
```
|
||||
|
||||
| 검증 | 실제 결과·증거 |
|
||||
| --- | --- |
|
||||
| `npm run lint` | **PASS**, ESLint + TypeScript strict 오류 0 · [로그](evidence/release-final-lint.log) |
|
||||
| `npm test` | **9 files / 57 tests passed**, skip 0 · [로그](evidence/release-final-test.log) |
|
||||
| `npm run build` | **PASS**, WAV 101개 SHA-256 및 root/public/dist 런타임 MIT notice 전문 대조 · [로그](evidence/release-final-build.log) |
|
||||
| 위 Chrome 전체 명령 | **12 passed (2.2m)**, 360px/1280px, 실제 재생·10문제 완주·재청취·404 복구·키보드·SNR·레이아웃·axe·크레딧·MIT HTTP 응답 · [로그](evidence/release-final-browser.log) |
|
||||
| sandbox 실제 실행 인자 | Chrome 4회 실행 모두 `--no-sandbox` 없음. opt-in 환경변수를 지운 상태로 실행 · [검증](evidence/release-integrity.log) |
|
||||
| `npm audit --audit-level=high` | **found 0 vulnerabilities**, 개발 의존성 포함 · [로그](evidence/release-final-audit.log) |
|
||||
| `npm ls --omit=dev` | React / ReactDOM 19.3.0, 해석 오류 없음 |
|
||||
| 브랜드·기록·정적 산출물 최종 확인 | 현재 UI·테스트·문서에 대체 브랜드 표기 없음, evidence 링크 존재, 101 WAV/notice 검증 통과 · [로그](evidence/release-integrity.log) |
|
||||
| `git diff --check` | 종료 코드 0. 시작 시 이미 소스 전체가 untracked였으므로 변경 텍스트의 후행 공백도 별도 검사 |
|
||||
|
||||
브라우저의 페이지 JS 예외·외부 페이지 요청·axe 위반은 모두 0이었다.
|
||||
Chrome 자체 진단 출력과 앱 페이지의 오류·네트워크 검증은 구별했다.
|
||||
실제 360px 게임 스크린샷에서도 올바른 브랜드, 질문 줄바꿈, 보기·음량·SNR 표시를 확인했다.
|
||||
|
||||
- [360px 시작](evidence/release-mobile-home.png), [게임](evidence/release-mobile-game.png), [결과](evidence/release-mobile-results.png)
|
||||
- [1280px 시작](evidence/release-desktop-home.png), [게임](evidence/release-desktop-game.png), [결과](evidence/release-desktop-results.png)
|
||||
|
||||
### 이번 변경 파일과 범위
|
||||
|
||||
- 앱·출제: `src/App.tsx`, `src/content.ts`, `src/game.ts`.
|
||||
- 라이선스·metadata·브라우저 설정: `LICENSE`, `THIRD_PARTY_NOTICES.md`,
|
||||
`public/THIRD_PARTY_NOTICES.md`, `package.json`, `package-lock.json`, `playwright.config.ts`.
|
||||
의존성 버전과 integrity는 바꾸지 않았다.
|
||||
- 생성·빌드: `scripts/generate_audio.py`, `scripts/check-build.mjs`,
|
||||
`public/audio/`의 101 WAV와 manifest. 안내 WAV도 다시 생성했으며 원문은 유지했다.
|
||||
- 회귀 검증: `tests/App.test.tsx`, `tests/content.test.ts`, `tests/game.test.ts`,
|
||||
`tests/assets.test.ts`, `tests/build.test.ts`, 새 `tests/release.test.ts`,
|
||||
`tests/playwright-config.test.ts`, `tests/browser/release.spec.ts`.
|
||||
기존 `tests/browser/game.spec.ts`·`ui.spec.ts`는 새 스크린샷 이름을 사용한다.
|
||||
- 문서·증거: `README.md`, `docs/ARCHITECTURE.md`, `docs/AUDIO_PROVENANCE.md`,
|
||||
`docs/DEPLOYMENT.md`, `.agent/CONTEXT.md`, `TASK.md`, `ACCEPTANCE.md`,
|
||||
이 `RESULT.md` 및 새 evidence 파일.
|
||||
|
||||
요청된 수정·자동 검증에 남은 실패는 없다. 한국어 합성 발음의 청감과 실물
|
||||
모바일 Safari는 기존과 같이 별도 인수 대상이다. WAV 용량은 약 20.5MB로 늘었고
|
||||
질문별로 로드한다. **서버 배포, Git commit, Git push는 실행하지 않았다.**
|
||||
|
||||
## Neo canonical 오디오 재시도 assertion 회귀 수정 — 2026-10-14 UTC
|
||||
|
||||
### RED 근거와 재현 조건
|
||||
|
||||
- **Neo가 보고한 기존 canonical 실패를 이번 RED 근거로 사용한다.**
|
||||
전체 `npm run test:browser`(12 tests, workers=2)에서 desktop의
|
||||
`오디오 HTTP 실패를 화면에 표시하고 재시도한다`가 재시도 후
|
||||
`getByRole("group", { name: "답 선택" })`를 global expect timeout
|
||||
**20,000ms** 안에 보지 못해 1회 실패했다.
|
||||
- 같은 수정 전 테스트를 desktop 단독 `--workers=1 --repeat-each=3`으로
|
||||
실행하면 **16.3~17.3초에 3회 모두 통과**했다는 Neo의 보고다.
|
||||
실패는 병렬 전체 실행에서 간헐적으로 발생했다. 이는 이번 로컬 실행의
|
||||
원문 로그가 아니며, 새로 실패를 재현했다고 주장하지 않는다.
|
||||
- 사용자 메시지에 첨부된 **12 passed (2.2m)** 출력은 성공 실행 기록이다.
|
||||
이를 실패 로그로 취급하지 않으며 기존 evidence도 보존한다.
|
||||
|
||||
### 원인 추적과 최소 수정 범위
|
||||
|
||||
`systematic-debugging` 절차로 실패한 assertion에서 상태 전이를 역추적했다.
|
||||
404가 난 최초 재생은 `src/App.tsx`의 `introDone`을 설정하지 않는다.
|
||||
재시도는 안내와 목표 WAV를 다시 로드·디코드하고, `src/audio/engine.ts`의
|
||||
목표 소스 `onended` → `play()` 완료 → `ENDED` → `answer` 상태가 된 뒤에야
|
||||
답 선택 그룹을 표시한다. 출제 seed는 `Date.now()`여서 실행별 목표 길이도
|
||||
달라진다. 정상 단독 실행과 병렬 실패의 차이는 이 대기 경로의 시간 여유다.
|
||||
|
||||
현재 101개 WAV를 Python `wave`의 프레임 수 / sample rate로 실측했다.
|
||||
안내는 **8.1205초**, 최장 목표 `message-market.wav`는 **10.1725초**다.
|
||||
엔진의 시작 여유 **0.05초**와 안내→목표 간격 **0.35초**를 더한 최대
|
||||
재생 일정은 **18.693초**다. 기존 20초 assertion에는 HTTP 로드·디코드·배경
|
||||
합성·브라우저 스케줄링·DOM 반영·assertion 관찰을 위한 여유가 **1.307초**만
|
||||
남는다. Neo의 병렬 실패와 단독 성공은 이 좁은 assertion 예산에 부합한다.
|
||||
당시 trace가 없어 실패 1회의 세부 지연 분포는 확인할 수 없다.
|
||||
[실측과 제어 흐름 기록](evidence/24-retry-audio-budget.log)
|
||||
|
||||
수정은 `tests/browser/game.spec.ts`의 **재시도 완료 `toBeVisible` 하나에만
|
||||
30,000ms 상한**을 지정한다. 최대 재생 일정 이후 **11.307초**의 여유를 두며
|
||||
조건이 충족되면 즉시 통과한다. 제품의 준비 제한 15초·재생 종료 후 5초
|
||||
watchdog, global expect 20초, test timeout 120초, workers=2는 유지한다.
|
||||
sleep, 자동 재시도, 오디오 mock, 테스트 skip, 제품 코드 변경은 추가하지 않는다.
|
||||
|
||||
### 수정 후 검증
|
||||
|
||||
환경: Node.js **26.7.0**, npm **11.19.0**, 시스템 Chrome
|
||||
`/usr/bin/google-chrome`. 새 production build에서 실제 WAV를 사용했다.
|
||||
브라우저 실행은 `env -u PLAYWRIGHT_NO_SANDBOX CHROME_PATH=/usr/bin/google-chrome`
|
||||
접두사로 기본 sandbox를 유지했다. 아래 명령은 모두 종료 코드 **0**이다.
|
||||
|
||||
| 검증 명령 | 실제 GREEN 결과 | 로그 |
|
||||
| --- | --- | --- |
|
||||
| `npm run lint` | **PASS**, ESLint + TypeScript 오류 0 | [lint](evidence/24-retry-final-lint.log) |
|
||||
| `npm test` | **9 files / 57 tests passed**, skip 0 | [test](evidence/24-retry-final-test.log) |
|
||||
| `npm run build` | **PASS**, WAV 101개와 런타임 notice 전문 확인 | [build](evidence/24-retry-final-build.log) |
|
||||
| `npm run test:browser -- tests/browser/game.spec.ts --project=desktop --workers=1 --repeat-each=3 --grep '오디오 HTTP 실패'` | **3 passed (52.9s)**, 각 **17.7s / 15.5s / 16.8s** | [targeted](evidence/24-retry-targeted-green.log) |
|
||||
| `npm run test:browser` | **12 passed (2.2m)**, desktop/mobile, workers=2, skip 0 | [browser](evidence/24-retry-final-browser.log) |
|
||||
|
||||
전체 병렬 실행의 재시도 테스트는 desktop **19.9초**, mobile **14.8초**에
|
||||
통과했다. 이 수치는 Playwright가 보고한 **테스트 전체 시간**이며 assertion
|
||||
자체의 대기 시간으로 해석하지 않는다. 실제 404 오류 표시→재시도→답 선택→
|
||||
처음으로 이동 후 초점 복귀 계약을 유지한 채 통과했다. 기존의 실제 오디오
|
||||
10문제 완주, 레이아웃·접근성·외부 요청 없음, 릴리스·UI 검사도 모두 통과했다.
|
||||
|
||||
이번 변경은 `tests/browser/game.spec.ts`의 assertion 한 곳과 이 기록,
|
||||
`evidence/24-retry-*.log`에 한정한다. 시작 시 소스 전체가 untracked였으므로
|
||||
작업 전 SHA-256 snapshot과 비교해 `src/`, 음원, 의존성, Playwright 설정 및
|
||||
기존 evidence가 그대로임을 확인했다. 전체 브라우저 실행이 다시 생성한
|
||||
기존 `release-*.png`는 작업 전 사본으로 복원해 과거 검증 증거를 보존했다.
|
||||
`git diff --check`, 수정 텍스트의 후행 공백 검사와 evidence 링크 확인도
|
||||
통과했다. [변경 범위와 무결성 확인](evidence/24-retry-integrity.log)
|
||||
|
||||
요청한 반복·전체 검증은 모두 GREEN이다. **Git commit, push, 배포는 실행하지 않았다.**
|
||||
@@ -0,0 +1,77 @@
|
||||
# TASK — Find-Word v0.1
|
||||
|
||||
## 역할
|
||||
|
||||
당신은 구현 담당 Codex다. 이 저장소에서만 작업하고 프로덕션 서버·DNS·인증정보에는 접근하지 않는다. Neo가 코드 리뷰·배포를 별도로 수행한다.
|
||||
|
||||
## 제품
|
||||
|
||||
- 서비스명: **단어를 맞춰라**
|
||||
- 표기: **Designed by OZ at Heat-T Medical Inc.**
|
||||
- 버전: **0.1**
|
||||
- 배포 예정 도메인: `findword.heart-m.com`
|
||||
- 언어: 사용자 UI와 상세 소스 주석은 한국어
|
||||
|
||||
## 목표
|
||||
|
||||
여러 환경 소음 속에 작은 음량으로 섞인 특정 한국어 단어나 문장을 듣고 질문에 답하는 웹 게임을 만든다. 쉬운 단계부터 소음 대비 키 문장의 dB 차이를 늘려 난이도를 높인다. 객관식과 주관식을 모두 지원한다.
|
||||
|
||||
## 핵심 시나리오
|
||||
|
||||
1. S 캐릭터가 “으하하하~~ 나는 에스다. 널 위한 행운의 단어를 불러주마, 잘 찾아보거라.”라고 안내한다.
|
||||
2. 웃음과 안내 음성이 배경 소음 속으로 점차 묻힌다.
|
||||
3. “오늘의 내 행운은 ‘사과’에 있다.” 같은 키 문장을 재생한다.
|
||||
4. 재생이 끝난 뒤 질문과 네 개의 선택지를 표시한다.
|
||||
5. 선택지는 정답 1개와 사전에 존재하는 유사어 오답 3개로 구성한다.
|
||||
6. 오답은 자음·모음 변형 규칙의 서로 다른 난이도를 반영하고, 데이터에 변형 종류를 명시한다.
|
||||
|
||||
## 기능 요구사항
|
||||
|
||||
- 반응형 SPA, 데스크톱·모바일 키보드/터치 지원
|
||||
- 시작 화면, 게임 화면, 결과 화면, 다시 듣기, 다음 문제, 전체 진행률
|
||||
- 사용자가 브라우저 정책상 필요한 “소리 시작” 조작을 직접 누르도록 설계
|
||||
- Web Audio API로 보이스와 배경을 믹싱하고 목표 음성 gain을 dB 기준으로 적용
|
||||
- 최소 5단계 난이도. 단계가 높아질수록 목표 문장과 배경의 상대 dB가 낮아짐
|
||||
- 최소 10개 문제 유형. 객관식·주관식을 포함하고 데이터 조합으로 실제 문제가 더 늘어남
|
||||
- 최소 8개 배경 환경: 길거리, 쇼핑몰, 극장, 백화점, 시장, 지하철, 카페, 비 오는 거리
|
||||
- 기본 패턴 문장, 시험 문장, 질문, 답, 오답 후보, 배경 프리셋을 저장된 데이터로 분리
|
||||
- 사전 단어만 사용하는 4지선다 데이터와 각 오답의 변형 규칙 메타데이터
|
||||
- 점수, 정답률, 연속 정답, 난이도별 결과
|
||||
- 접근성: aria, 명확한 focus, 키보드 조작, reduced motion, 색 대비
|
||||
- 오디오가 없거나 로드 실패해도 사용자에게 명확한 오류와 재시도 제공
|
||||
- 개인 데이터 수집·로그인·추적기 없음
|
||||
- 버전 0.1을 UI와 package metadata에 표시
|
||||
|
||||
## 오디오 요구사항
|
||||
|
||||
- 라이선스 불명확한 외부 자산을 사용하지 않는다.
|
||||
- 직접 생성한 배경 소음·효과음을 저장하거나, 브라우저에서 결정론적으로 합성하는 프리셋을 사용한다.
|
||||
- 한국어 목표 음성은 가능하면 직접 생성한 오디오 파일로 저장한다. 네트워크 TTS를 사용했다면 생성 스크립트·출처·생성 절차를 문서화하고 앱은 런타임에 외부 TTS를 호출하지 않는다.
|
||||
- 모든 자산의 provenance를 `docs/AUDIO_PROVENANCE.md`에 기록한다.
|
||||
- 오디오 파일을 커밋할 경우 크기를 합리적으로 제한하고 build 결과에 포함되는지 테스트한다.
|
||||
|
||||
## 개발 규칙
|
||||
|
||||
- strict TDD: 각 동작은 실패하는 테스트를 먼저 작성하고 실제 RED→GREEN 근거를 `.agent/RESULT.md`에 기록한다.
|
||||
- TypeScript strict mode를 사용한다.
|
||||
- 모든 직접 작성 소스 코드에 상세한 한국어 주석을 단다. 자명한 문법 줄마다 반복 주석을 붙이지 말고 알고리즘, 상태, 오디오, 난이도, 접근성, 보안 의도를 설명한다.
|
||||
- generated lockfile과 기계 생성 바이너리는 주석 대상이 아니다.
|
||||
- 비밀값, 서버 자격증명, API 키를 저장하지 않는다.
|
||||
- 서버 배포는 하지 않는다.
|
||||
- Git 커밋은 하지 않는다. Neo가 검증 후 커밋한다.
|
||||
|
||||
## 권장 기술
|
||||
|
||||
- Vite + React + TypeScript
|
||||
- Vitest + Testing Library
|
||||
- 순수 CSS 또는 가벼운 CSS 구조
|
||||
- 정적 빌드(`dist/`)로 Nginx 배포 가능
|
||||
|
||||
## 산출물
|
||||
|
||||
- 동작하는 v0.1 소스
|
||||
- 최소 10개 문제 유형과 조합 가능한 데이터
|
||||
- 오디오 생성·재생 구조
|
||||
- unit/component 테스트
|
||||
- `README.md`, `docs/ARCHITECTURE.md`, `docs/AUDIO_PROVENANCE.md`, `docs/DEPLOYMENT.md`
|
||||
- `.agent/RESULT.md`: 변경 파일, RED/GREEN 명령과 실제 결과, 전체 검증 결과, 미해결 위험
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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
|
||||
[36m<input[39m
|
||||
[33mchecked[39m=[32m""[39m
|
||||
[33mname[39m=[32m"mode"[39m
|
||||
[33mtype[39m=[32m"radio"[39m
|
||||
[33mvalue[39m=[32m"mixed"[39m
|
||||
[36m/>[39m
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<input[39m
|
||||
[33mname[39m=[32m"mode"[39m
|
||||
[33mtype[39m=[32m"radio"[39m
|
||||
[33mvalue[39m=[32m"text"[39m
|
||||
[36m/>[39m
|
||||
|
||||
(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<a[39m
|
||||
[33mclass[39m=[32m"skip-link"[39m
|
||||
[33mhref[39m=[32m"#main"[39m
|
||||
[36m>[39m
|
||||
[0m본문으로 건너뛰기[0m
|
||||
[36m</a>[39m
|
||||
[36m<header[39m
|
||||
[33mclass[39m=[32m"header wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"brand"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"brand-mark"[39m
|
||||
[36m>[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m</span>[39m
|
||||
[36m<h1>[39m
|
||||
[0m단어를 맞춰라[0m
|
||||
[36m</h1>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"version"[39m
|
||||
[36m>[39m
|
||||
[0mv0.1[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"header-actions"[39m
|
||||
[36m>[39m
|
||||
[36m<a[39m
|
||||
[33mhref[39m=[32m"#howto"[39m
|
||||
[36m>[39m
|
||||
[0m플레이 방법 [0m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m↗[0m
|
||||
[36m</span>[39m
|
||||
[36m</a>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"little-dot"[39m
|
||||
[36m/>[39m
|
||||
[0m소리에 집중하는 시간[0m
|
||||
[36m</div>[39m
|
||||
[36m</header>[39m
|
||||
[36m<main[39m
|
||||
[33mclass[39m=[32m"wrap"[39m
|
||||
[33mid[39m=[32m"main"[39m
|
||||
[33mtabindex[39m=[32m"-1"[39m
|
||||
[36m>[39m
|
||||
[36m<section[39m
|
||||
[33maria-labelledby[39m=[32m"hero-title"[39m
|
||||
[33mclass[39m=[32m"hero"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"hero-copy"[39m
|
||||
[36m>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"eyebrow"[39m
|
||||
[36m>[39m
|
||||
[36m<span />[39m
|
||||
[0m 귀를 기울이면, 발견하는 즐거움[0m
|
||||
[36m</p>[39m
|
||||
[36m<h2[39m
|
||||
[33mid[39m=[32m"hero-title"[39m
|
||||
[36m>[39m
|
||||
[0m소음 속 작은 단어,[0m
|
||||
[36m<br />[39m
|
||||
[36m<em>[39m
|
||||
[0m들리시나요?[0m
|
||||
[36m</em>[39m
|
||||
[36m</h2>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"lead"[39m
|
||||
[36m>[39m
|
||||
[0m도시의 소리 사이에 에스가 숨겨 둔 단어.[0m
|
||||
[36m<br />[39m
|
||||
[0m조금 더 귀 기울여, 오늘의 행운을 찾아보세요.[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"hero-meta"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m10개의 질문[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m5단계 도전[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m나만의 속도로[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"sound-room"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"room-top"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[36m<i />[39m
|
||||
[0m 에스의 사운드 룸[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m01 / 08[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"orbit orbit-one"[39m
|
||||
[36m/>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"orbit orbit-two"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"floating-note note-one"[39m
|
||||
[36m>[39m
|
||||
[0m♪[0m
|
||||
[36m</span>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"floating-note note-two"[39m
|
||||
[36m>[39m
|
||||
[0m✳[0m
|
||||
[36m</span>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"speech-bubble"[39m
|
||||
[36m>[39m
|
||||
[0m행운의 단어를 찾아보거라![0m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mascot "[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"smile"[39m
|
||||
[36m/>[39m
|
||||
[36m<b>[39m
|
||||
[0mS[0m
|
||||
[36m</b>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"room-bottom"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"sound-wave "[39m
|
||||
[36m>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 10px; animation-delay: 0s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 33.799955450859244px; animation-delay: 0.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.132986448643948px; animation-delay: 0.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 21.858720427326595px; animation-delay: 0.2s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.163690702963766px; animation-delay: 0.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.619883556555898px; animation-delay: 0.4s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 35.39139668035928px; animation-delay: 0.45s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 71.52943948029164px; animation-delay: 0.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 19.53593765210872px; animation-delay: 0.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.952742107140878px; animation-delay: 0.65s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.979734657418774px; animation-delay: 0.75s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 66.27347594564635px; animation-delay: 0.8s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 56.615089141222136px; animation-delay: 0.9s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 43.861291911681526px; animation-delay: 1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 68.21866699734562px; animation-delay: 1.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 28.858982496016484px; animation-delay: 1.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.74953619965402px; animation-delay: 1.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 23.8231850360459px; animation-delay: 1.3s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.57029511072814px; animation-delay: 1.35s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.085500220240995px; animation-delay: 1.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 25.59070868046883px; animation-delay: 1.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.10305417152866px; animation-delay: 1.6s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m</div>[39m
|
||||
[36m<span>[39m
|
||||
[0m소음 사이, 숨겨진 한마디[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</section>[39m
|
||||
[36m<section[39m
|
||||
[33maria-labelledby[39m=[32m"setup-title"[39m
|
||||
[33mclass[39m=[32m"setup-card"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"setup-heading"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"section-number"[39m
|
||||
[36m>[39m
|
||||
[0m01[0m
|
||||
[36m</span>[39m
|
||||
[36m<div>[39m
|
||||
[36m<h2[39m
|
||||
[33mid[39m=[32m"setup-title"[39m
|
||||
[36m>[39m
|
||||
[0m나의 소리 탐험 준비[0m
|
||||
[36m</h2>[39m
|
||||
[36m<p>[39m
|
||||
[0m편한 방식으로 시작하세요. 난이도는 조금씩 높아져요.[0m
|
||||
[36m</p>[39m
|
||||
[36m</div>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"round-badge"[39m
|
||||
[36m>[39m
|
||||
[0m약 3–5분[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"setup-grid"[39m
|
||||
[36m>[39m
|
||||
[36m<fieldset[39m
|
||||
[33mclass[39m=[32m"mode-field"[39m
|
||||
[36m>[39m
|
||||
[36m<legend>[39m
|
||||
[0m어떻게 답할까요?[0m
|
||||
[36m</legend>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"mode-options"[39m
|
||||
[36m>[39m
|
||||
[36m<label[39m
|
||||
[33mclass[39m=[32m"mode-option selected"[39m
|
||||
[36m>[39m
|
||||
[36m<input[39m
|
||||
[33mchecked[39m=[32m""[39m
|
||||
[33mname[39m=[32m"mode"[39m
|
||||
[33mtype[39m=[32m"radio"[39m
|
||||
[33mvalue[39m=[32m"mixed"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mode-icon"[39m
|
||||
[36m>[39m
|
||||
[0m◈[0m
|
||||
[36m</span>[39m
|
||||
[36m<strong>[39m
|
||||
[0m골고루 풀기[0m
|
||||
[36m</strong>[39m
|
||||
[36m<small>[39m
|
||||
[0m선택 + 직접 입력[0m
|
||||
[36m</small>[39m
|
||||
[36m</label>[39m
|
||||
[36m<label[39m
|
||||
[33mclass[39m=[32m"mode-option "[39m
|
||||
[36m>[39m
|
||||
[36m<input[39m
|
||||
[33mname[39m=[32m"mode"[39m
|
||||
[33mtype[39m=[32m"radio"[39m
|
||||
[33mvalue[39m=[32m"choice"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mode-icon"[39m
|
||||
[36m>[39m
|
||||
[0m☷[0m
|
||||
[36m</span>[39m
|
||||
[36m<strong>[39m
|
||||
[0m보기에서 선택[0m
|
||||
[36m</strong>[39m
|
||||
[36m<small>[39m
|
||||
[0m4개 단어 중 하나[0m
|
||||
[36m</small>[39m
|
||||
[36m</label>[39m
|
||||
[36m<label[39m
|
||||
[33mclass[39m=[32m"mode-option "[39m
|
||||
[36m>[39m
|
||||
[36m<input[39m
|
||||
[33mname[39m=[32m"mode"[39m
|
||||
[33mtype[39m=[32m"radio"[39m
|
||||
[33mvalue[39m=[32m"text"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mode-icon"[39m
|
||||
[36m>[39m
|
||||
[0m✎[0m
|
||||
[36m</span>[39m
|
||||
[36m<strong>[39m
|
||||
[0m직접 입력[0m
|
||||
[36m</strong>[39m
|
||||
[36m<small>[39m
|
||||
[0m기억한 단어 쓰기[0m
|
||||
[36m</small>[39m
|
||||
[36m</label>[39m
|
||||
[36m</div>[39m
|
||||
[36m</fieldset>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"environment-field"[39m
|
||||
[36m>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"environment"[39m
|
||||
[36m>[39m
|
||||
[0m배경 환경[0m
|
||||
[36m</label>[39m
|
||||
[36m<select[39m
|
||||
[33mid[39m=[32m"environment"[39m
|
||||
[36m>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"rotate"[39m
|
||||
[36m>[39m
|
||||
[0m여덟 가지 일상 속으로[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"street"[39m
|
||||
[36m>[39m
|
||||
[0m길거리[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"mall"[39m
|
||||
[36m>[39m
|
||||
[0m쇼핑몰[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"theater"[39m
|
||||
[36m>[39m
|
||||
[0m극장[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"department"[39m
|
||||
[36m>[39m
|
||||
[0m백화점[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"market"[39m
|
||||
[36m>[39m
|
||||
[0m시장[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"subway"[39m
|
||||
[36m>[39m
|
||||
[0m지하철[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"cafe"[39m
|
||||
[36m>[39m
|
||||
[0m카페[0m
|
||||
[36m</option>[39m
|
||||
[36m<option[39m
|
||||
[33mvalue[39m=[32m"rain"[39m
|
||||
[36m>[39m
|
||||
[0m비 오는 거리[0m
|
||||
[36m</option>[39m
|
||||
[36m</select>[39m
|
||||
[36m<p>[39m
|
||||
[0m길거리부터 비 오는 거리까지, 매번 다른 공간을 만나요.[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"environment-chips"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m길거리[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m카페[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m지하철[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m+5개 환경[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"start-row"[39m
|
||||
[36m>[39m
|
||||
[36m<p>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m♬[0m
|
||||
[36m</span>[39m
|
||||
[0m 편안한 작은 음량으로 시작해 보세요.[0m
|
||||
[36m</p>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"primary"[39m
|
||||
[36m>[39m
|
||||
[0m소리 시작 [0m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m→[0m
|
||||
[36m</span>[39m
|
||||
[36m</button>[39m
|
||||
[36m</div>[39m
|
||||
[36m</section>[39m
|
||||
[36m<section[39m
|
||||
[33maria-labelledby[39m=[32m"howto-title"[39m
|
||||
[33mclass[39m=[32m"howto"[39m
|
||||
[33mid[39m=[32m"howto"[39m
|
||||
[36m>[39m
|
||||
[36m<h2[39m
|
||||
[33mid[39m=[32m"howto-title"[39m
|
||||
[36m>[39m
|
||||
[0m작은 단어를 발견하는 방법[0m
|
||||
[36m</h2>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"steps"[39m
|
||||
[36m>[39m
|
||||
[36m<article>[39m
|
||||
[36m<span>[39m
|
||||
[0m01[0m
|
||||
[36m</span>[39m
|
||||
[36m<h3>[39m
|
||||
[0m먼저, 귀 기울이기[0m
|
||||
[36m</h3>[39m
|
||||
[36m<p>[39m
|
||||
[0m에스의 안내와 소음 속 문장을 들어요.[0m
|
||||
[36m<br />[39m
|
||||
[0m처음에는 또렷하게, 점점 더 작게.[0m
|
||||
[36m</p>[39m
|
||||
[36m</article>[39m
|
||||
[36m<article>[39m
|
||||
[36m<span>[39m
|
||||
[0m02[0m
|
||||
[36m</span>[39m
|
||||
[36m<h3>[39m
|
||||
[0m기억한 단어 찾기[0m
|
||||
[36m</h3>[39m
|
||||
[36m<p>[39m
|
||||
[0m소리가 끝나면 질문이 나타나요.[0m
|
||||
[36m<br />[39m
|
||||
[0m보기에서 고르거나 직접 입력해요.[0m
|
||||
[36m</p>[39m
|
||||
[36m</article>[39m
|
||||
[36m<article>[39m
|
||||
[36m<span>[39m
|
||||
[0m03[0m
|
||||
[36m</span>[39m
|
||||
[36m<h3>[39m
|
||||
[0m나의 발견 돌아보기[0m
|
||||
[36m</h3>[39m
|
||||
[36m<p>[39m
|
||||
[0m열 문제 뒤 나의 결과를 확인해요.[0m
|
||||
[36m<br />[39m
|
||||
[0m놓쳤다면 언제든 다시 들어도 좋아요.[0m
|
||||
[36m</p>[39m
|
||||
[36m</article>[39m
|
||||
[36m</div>[39m
|
||||
[36m</section>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"volume-row"[39m
|
||||
[36m>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m◖))[0m
|
||||
[36m</span>[39m
|
||||
[0m 전체 음량[0m
|
||||
[36m</label>[39m
|
||||
[36m<input[39m
|
||||
[33mid[39m=[32m"volume"[39m
|
||||
[33mmax[39m=[32m"100"[39m
|
||||
[33mmin[39m=[32m"0"[39m
|
||||
[33mtype[39m=[32m"range"[39m
|
||||
[33mvalue[39m=[32m"35"[39m
|
||||
[36m/>[39m
|
||||
[36m<output[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[0m35[0m
|
||||
[0m%[0m
|
||||
[36m</output>[39m
|
||||
[36m</div>[39m
|
||||
[36m</main>[39m
|
||||
[36m<footer[39m
|
||||
[33mclass[39m=[32m"footer wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<p>[39m
|
||||
[0mDesigned by OZ at HearT-M Medical Inc.[0m
|
||||
[36m</p>[39m
|
||||
[36m<p>[39m
|
||||
[0m청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.[0m
|
||||
[36m</p>[39m
|
||||
[36m<span>[39m
|
||||
[0m개인정보 수집 없이, 오직 플레이만.[0m
|
||||
[36m</span>[39m
|
||||
[36m</footer>[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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%)
|
||||
@@ -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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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 "":
|
||||
[36m<main />[39m
|
||||
|
||||
--------------------------------------------------
|
||||
|
||||
Ignored nodes: comments, script, style
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<main />[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<a[39m
|
||||
[33mclass[39m=[32m"skip-link"[39m
|
||||
[33mhref[39m=[32m"#main"[39m
|
||||
[36m>[39m
|
||||
[0m본문으로 건너뛰기[0m
|
||||
[36m</a>[39m
|
||||
[36m<header[39m
|
||||
[33mclass[39m=[32m"header wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"brand"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"brand-mark"[39m
|
||||
[36m>[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m</span>[39m
|
||||
[36m<h1>[39m
|
||||
[0m단어를 맞춰라[0m
|
||||
[36m</h1>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"version"[39m
|
||||
[36m>[39m
|
||||
[0mv0.1[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"header-actions"[39m
|
||||
[36m>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"text-button"[39m
|
||||
[36m>[39m
|
||||
[0m처음으로[0m
|
||||
[36m</button>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"little-dot"[39m
|
||||
[36m/>[39m
|
||||
[0m소리에 집중하는 시간[0m
|
||||
[36m</div>[39m
|
||||
[36m</header>[39m
|
||||
[36m<main[39m
|
||||
[33mclass[39m=[32m"wrap"[39m
|
||||
[33mid[39m=[32m"main"[39m
|
||||
[33mtabindex[39m=[32m"-1"[39m
|
||||
[36m>[39m
|
||||
[36m<section[39m
|
||||
[33maria-label[39m=[32m"단어 찾기 게임"[39m
|
||||
[33mclass[39m=[32m"game-layout"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"game-top"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eyebrow"[39m
|
||||
[36m>[39m
|
||||
[0m나의 소리 탐험[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m문제 [0m
|
||||
[36m<b>[39m
|
||||
[0m01[0m
|
||||
[36m</b>[39m
|
||||
[0m / 10[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<progress[39m
|
||||
[33maria-label[39m=[32m"전체 진행률"[39m
|
||||
[33mmax[39m=[32m"10"[39m
|
||||
[33mvalue[39m=[32m"0"[39m
|
||||
[36m/>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"game-columns"[39m
|
||||
[36m>[39m
|
||||
[36m<aside[39m
|
||||
[33mclass[39m=[32m"environment-panel"[39m
|
||||
[36m>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"eyebrow"[39m
|
||||
[36m>[39m
|
||||
[0m지금 듣고 있는 공간[0m
|
||||
[36m</p>[39m
|
||||
[36m<h2>[39m
|
||||
[0m길거리[0m
|
||||
[36m</h2>[39m
|
||||
[36m<p>[39m
|
||||
[0m지나가는 차와 도시의 웅성임[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mascot small"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"smile"[39m
|
||||
[36m/>[39m
|
||||
[36m<b>[39m
|
||||
[0mS[0m
|
||||
[36m</b>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"sound-wave "[39m
|
||||
[36m>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 10px; animation-delay: 0s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 33.799955450859244px; animation-delay: 0.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.132986448643948px; animation-delay: 0.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 21.858720427326595px; animation-delay: 0.2s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.163690702963766px; animation-delay: 0.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.619883556555898px; animation-delay: 0.4s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 35.39139668035928px; animation-delay: 0.45s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 71.52943948029164px; animation-delay: 0.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 19.53593765210872px; animation-delay: 0.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.952742107140878px; animation-delay: 0.65s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.979734657418774px; animation-delay: 0.75s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 66.27347594564635px; animation-delay: 0.8s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 56.615089141222136px; animation-delay: 0.9s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 43.861291911681526px; animation-delay: 1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 68.21866699734562px; animation-delay: 1.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 28.858982496016484px; animation-delay: 1.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.74953619965402px; animation-delay: 1.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 23.8231850360459px; animation-delay: 1.3s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.57029511072814px; animation-delay: 1.35s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.085500220240995px; animation-delay: 1.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 25.59070868046883px; animation-delay: 1.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.10305417152866px; animation-delay: 1.6s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"level-tag"[39m
|
||||
[36m>[39m
|
||||
[0m1[0m
|
||||
[0m단계 · [0m
|
||||
[0m+[0m
|
||||
[0m6[0m
|
||||
[0m dB[0m
|
||||
[36m</div>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"db-note"[39m
|
||||
[36m>[39m
|
||||
[0m음성 / 배경의 상대 음량[0m
|
||||
[36m<br />[39m
|
||||
[0m소리에 익숙해지기[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"mini-stats"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m점수 [0m
|
||||
[36m<b>[39m
|
||||
[0m0[0m
|
||||
[36m</b>[39m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m연속 정답 [0m
|
||||
[36m<b>[39m
|
||||
[0m0[0m
|
||||
[36m</b>[39m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m</aside>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"question-panel panel"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"question-meta"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m마지막 단어[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m주관식[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<h2[39m
|
||||
[33mtabindex[39m=[32m"-1"[39m
|
||||
[36m>[39m
|
||||
[0m마지막에 나온 단어는 무엇인가요?[0m
|
||||
[36m</h2>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"muted"[39m
|
||||
[36m>[39m
|
||||
[0m기억나는 단어 하나를 입력해 주세요.[0m
|
||||
[36m</p>[39m
|
||||
[36m<form>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"answer"[39m
|
||||
[36m>[39m
|
||||
[0m들은 단어[0m
|
||||
[36m</label>[39m
|
||||
[36m<input[39m
|
||||
[33mautocomplete[39m=[32m"off"[39m
|
||||
[33mid[39m=[32m"answer"[39m
|
||||
[33mmaxlength[39m=[32m"60"[39m
|
||||
[33mplaceholder[39m=[32m"여기에 단어를 적어 주세요"[39m
|
||||
[33mvalue[39m=[32m"기름"[39m
|
||||
[36m/>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"primary"[39m
|
||||
[36m>[39m
|
||||
[0m정답 확인[0m
|
||||
[36m</button>[39m
|
||||
[36m</form>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"play-controls"[39m
|
||||
[36m>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"secondary"[39m
|
||||
[36m>[39m
|
||||
[0m↻ 다시 듣기[0m
|
||||
[36m</button>[39m
|
||||
[36m<small>[39m
|
||||
[0m다시 들어도 점수는 줄어들지 않아요.[0m
|
||||
[36m</small>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</section>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"volume-row"[39m
|
||||
[36m>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m◖))[0m
|
||||
[36m</span>[39m
|
||||
[0m 전체 음량[0m
|
||||
[36m</label>[39m
|
||||
[36m<input[39m
|
||||
[33mid[39m=[32m"volume"[39m
|
||||
[33mmax[39m=[32m"100"[39m
|
||||
[33mmin[39m=[32m"0"[39m
|
||||
[33mtype[39m=[32m"range"[39m
|
||||
[33mvalue[39m=[32m"35"[39m
|
||||
[36m/>[39m
|
||||
[36m<output[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[0m35[0m
|
||||
[0m%[0m
|
||||
[36m</output>[39m
|
||||
[36m</div>[39m
|
||||
[36m</main>[39m
|
||||
[36m<footer[39m
|
||||
[33mclass[39m=[32m"footer wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<p>[39m
|
||||
[0mDesigned by OZ at HearT-M Medical Inc.[0m
|
||||
[36m</p>[39m
|
||||
[36m<p>[39m
|
||||
[0m청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.[0m
|
||||
[36m</p>[39m
|
||||
[36m<span>[39m
|
||||
[0m개인정보 수집 없이, 오직 플레이만.[0m
|
||||
[36m</span>[39m
|
||||
[36m</footer>[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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
|
||||
[36m<body>[39m
|
||||
[36m<div>[39m
|
||||
[36m<a[39m
|
||||
[33mclass[39m=[32m"skip-link"[39m
|
||||
[33mhref[39m=[32m"#main"[39m
|
||||
[36m>[39m
|
||||
[0m본문으로 건너뛰기[0m
|
||||
[36m</a>[39m
|
||||
[36m<header[39m
|
||||
[33mclass[39m=[32m"header wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"brand"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"brand-mark"[39m
|
||||
[36m>[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m<i />[39m
|
||||
[36m</span>[39m
|
||||
[36m<h1>[39m
|
||||
[0m단어를 맞춰라[0m
|
||||
[36m</h1>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"version"[39m
|
||||
[36m>[39m
|
||||
[0mv0.1[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"header-actions"[39m
|
||||
[36m>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"text-button"[39m
|
||||
[36m>[39m
|
||||
[0m처음으로[0m
|
||||
[36m</button>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"little-dot"[39m
|
||||
[36m/>[39m
|
||||
[0m소리에 집중하는 시간[0m
|
||||
[36m</div>[39m
|
||||
[36m</header>[39m
|
||||
[36m<main[39m
|
||||
[33mclass[39m=[32m"wrap"[39m
|
||||
[33mid[39m=[32m"main"[39m
|
||||
[33mtabindex[39m=[32m"-1"[39m
|
||||
[36m>[39m
|
||||
[36m<section[39m
|
||||
[33maria-label[39m=[32m"단어 찾기 게임"[39m
|
||||
[33mclass[39m=[32m"game-layout"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"game-top"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eyebrow"[39m
|
||||
[36m>[39m
|
||||
[0m나의 소리 탐험[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m문제 [0m
|
||||
[36m<b>[39m
|
||||
[0m01[0m
|
||||
[36m</b>[39m
|
||||
[0m / 10[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<progress[39m
|
||||
[33maria-label[39m=[32m"전체 진행률"[39m
|
||||
[33mmax[39m=[32m"10"[39m
|
||||
[33mvalue[39m=[32m"0"[39m
|
||||
[36m/>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"game-columns"[39m
|
||||
[36m>[39m
|
||||
[36m<aside[39m
|
||||
[33mclass[39m=[32m"environment-panel"[39m
|
||||
[36m>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"eyebrow"[39m
|
||||
[36m>[39m
|
||||
[0m지금 듣고 있는 공간[0m
|
||||
[36m</p>[39m
|
||||
[36m<h2>[39m
|
||||
[0m길거리[0m
|
||||
[36m</h2>[39m
|
||||
[36m<p>[39m
|
||||
[0m지나가는 차와 도시의 웅성임[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"mascot small"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"ear right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye left"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"eye right"[39m
|
||||
[36m/>[39m
|
||||
[36m<span[39m
|
||||
[33mclass[39m=[32m"smile"[39m
|
||||
[36m/>[39m
|
||||
[36m<b>[39m
|
||||
[0mS[0m
|
||||
[36m</b>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[33mclass[39m=[32m"sound-wave "[39m
|
||||
[36m>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 10px; animation-delay: 0s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 33.799955450859244px; animation-delay: 0.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.132986448643948px; animation-delay: 0.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.219552375865575px; animation-delay: 0.15000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 21.858720427326595px; animation-delay: 0.2s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.163690702963766px; animation-delay: 0.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.796992502245015px; animation-delay: 0.30000000000000004s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 24.835290693688798px; animation-delay: 0.35000000000000003s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.619883556555898px; animation-delay: 0.4s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 35.39139668035928px; animation-delay: 0.45s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 71.52943948029164px; animation-delay: 0.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 19.53593765210872px; animation-delay: 0.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.98674560913082px; animation-delay: 0.6000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 16.952742107140878px; animation-delay: 0.65s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 72.19510052760725px; animation-delay: 0.7000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.979734657418774px; animation-delay: 0.75s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 66.27347594564635px; animation-delay: 0.8s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 47.480789758355044px; animation-delay: 0.8500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 56.615089141222136px; animation-delay: 0.9s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 59.492984932238116px; animation-delay: 0.9500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 43.861291911681526px; animation-delay: 1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 68.21866699734562px; animation-delay: 1.05s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 28.858982496016484px; animation-delay: 1.1s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.07841912260051px; animation-delay: 1.1500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 12.60436847039135px; animation-delay: 1.2000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 73.74953619965402px; animation-delay: 1.25s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 23.8231850360459px; animation-delay: 1.3s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.57029511072814px; animation-delay: 1.35s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.99981090780238px; animation-delay: 1.4000000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 29.735764963203792px; animation-delay: 1.4500000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 26.085500220240995px; animation-delay: 1.5s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 25.59070868046883px; animation-delay: 1.55s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 30.10305417152866px; animation-delay: 1.6s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 20.410372983416416px; animation-delay: 1.6500000000000001s;"[39m
|
||||
[36m/>[39m
|
||||
[36m<i[39m
|
||||
[33mstyle[39m=[32m"height: 32.785692656480826px; animation-delay: 1.7000000000000002s;"[39m
|
||||
[36m/>[39m
|
||||
[36m</div>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"level-tag"[39m
|
||||
[36m>[39m
|
||||
[0m1[0m
|
||||
[0m단계 · [0m
|
||||
[0m+[0m
|
||||
[0m6[0m
|
||||
[0m dB[0m
|
||||
[36m</div>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"db-note"[39m
|
||||
[36m>[39m
|
||||
[0m음성 / 배경의 상대 음량[0m
|
||||
[36m<br />[39m
|
||||
[0m소리에 익숙해지기[0m
|
||||
[36m</p>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"mini-stats"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m점수 [0m
|
||||
[36m<b>[39m
|
||||
[0m0[0m
|
||||
[36m</b>[39m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m연속 정답 [0m
|
||||
[36m<b>[39m
|
||||
[0m0[0m
|
||||
[36m</b>[39m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m</aside>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"question-panel panel"[39m
|
||||
[36m>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"question-meta"[39m
|
||||
[36m>[39m
|
||||
[36m<span>[39m
|
||||
[0m마지막 단어[0m
|
||||
[36m</span>[39m
|
||||
[36m<span>[39m
|
||||
[0m주관식[0m
|
||||
[36m</span>[39m
|
||||
[36m</div>[39m
|
||||
[36m<h2[39m
|
||||
[33mtabindex[39m=[32m"-1"[39m
|
||||
[36m>[39m
|
||||
[0m마지막에 나온 단어는 무엇인가요?[0m
|
||||
[36m</h2>[39m
|
||||
[36m<p[39m
|
||||
[33mclass[39m=[32m"muted"[39m
|
||||
[36m>[39m
|
||||
[0m기억나는 단어 하나를 입력해 주세요.[0m
|
||||
[36m</p>[39m
|
||||
[36m<form>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"answer"[39m
|
||||
[36m>[39m
|
||||
[0m들은 단어[0m
|
||||
[36m</label>[39m
|
||||
[36m<input[39m
|
||||
[33mautocomplete[39m=[32m"off"[39m
|
||||
[33mid[39m=[32m"answer"[39m
|
||||
[33mmaxlength[39m=[32m"60"[39m
|
||||
[33mplaceholder[39m=[32m"여기에 단어를 적어 주세요"[39m
|
||||
[33mvalue[39m=[32m"기름"[39m
|
||||
[36m/>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"primary"[39m
|
||||
[36m>[39m
|
||||
[0m정답 확인[0m
|
||||
[36m</button>[39m
|
||||
[36m</form>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"play-controls"[39m
|
||||
[36m>[39m
|
||||
[36m<button[39m
|
||||
[33mclass[39m=[32m"secondary"[39m
|
||||
[36m>[39m
|
||||
[0m↻ 다시 듣기[0m
|
||||
[36m</button>[39m
|
||||
[36m<small>[39m
|
||||
[0m다시 들어도 점수는 줄어들지 않아요.[0m
|
||||
[36m</small>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</div>[39m
|
||||
[36m</section>[39m
|
||||
[36m<div[39m
|
||||
[33mclass[39m=[32m"volume-row"[39m
|
||||
[36m>[39m
|
||||
[36m<label[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[36m<span[39m
|
||||
[33maria-hidden[39m=[32m"true"[39m
|
||||
[36m>[39m
|
||||
[0m◖))[0m
|
||||
[36m</span>[39m
|
||||
[0m 전체 음량[0m
|
||||
[36m</label>[39m
|
||||
[36m<input[39m
|
||||
[33mid[39m=[32m"volume"[39m
|
||||
[33mmax[39m=[32m"100"[39m
|
||||
[33mmin[39m=[32m"0"[39m
|
||||
[33mtype[39m=[32m"range"[39m
|
||||
[33mvalue[39m=[32m"35"[39m
|
||||
[36m/>[39m
|
||||
[36m<output[39m
|
||||
[33mfor[39m=[32m"volume"[39m
|
||||
[36m>[39m
|
||||
[0m35[0m
|
||||
[0m%[0m
|
||||
[36m</output>[39m
|
||||
[36m</div>[39m
|
||||
[36m</main>[39m
|
||||
[36m<footer[39m
|
||||
[33mclass[39m=[32m"footer wrap"[39m
|
||||
[36m>[39m
|
||||
[36m<p>[39m
|
||||
[0mDesigned by OZ at HearT-M Medical Inc.[0m
|
||||
[36m</p>[39m
|
||||
[36m<p>[39m
|
||||
[0m청각적 주의·식별 게임 · 의료적 진단이나 치료를 제공하지 않습니다.[0m
|
||||
[36m</p>[39m
|
||||
[36m<span>[39m
|
||||
[0m개인정보 수집 없이, 오직 플레이만.[0m
|
||||
[36m</span>[39m
|
||||
[36m</footer>[39m
|
||||
[36m</div>[39m
|
||||
[36m</body>[39m
|
||||
❯ 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개 확인
|
||||
@@ -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)
|
||||
@@ -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 › 한국어 제목은 단어 중간에서 줄바꿈하지 않는다 ────────────────────────────
|
||||
@@ -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)
|
||||
@@ -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 › 난이도 설명은 모바일에서도 보인다 ───────────────────────────────────
|
||||
@@ -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개 확인
|
||||
@@ -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%)
|
||||
@@ -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)
|
||||
@@ -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 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ───────────────────────
|
||||
@@ -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개 확인
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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)
|
||||
@@ -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 › 전체 음량은 설정 흐름 안에 들어가며 키보드로 조절된다 ───────────────────────
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
> find-word@0.1.0 audio:generate
|
||||
> python3 scripts/generate_audio.py
|
||||
|
||||
101개 WAV 생성, 합계 20,497,222 바이트
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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 전문을 제공한다 ───────────
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
$ npm run lint
|
||||
|
||||
> find-word@0.1.0 lint
|
||||
> eslint . && tsc --noEmit
|
||||
|
||||
|
||||
Exit code: 0
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 모두 이전과 동일
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1 @@
|
||||
found 0 vulnerabilities
|
||||
@@ -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)
|
||||
@@ -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개 확인
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
> find-word@0.1.0 lint
|
||||
> eslint . && tsc --noEmit
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
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)
|
||||
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 72 KiB |
@@ -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)
|
||||
@@ -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개 확인
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
> find-word@0.1.0 lint
|
||||
> eslint . && tsc --noEmit
|
||||
@@ -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'.
|
||||
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 116 KiB |