| name | test |
| description | Analyze test coverage gaps (unit + E2E), create missing tests using worker subagents, then run and fix all tests. Uses Explore subagent for analysis and Worker subagents for parallel test creation. |
| disable-model-invocation | true |
| allowed-tools | Read, Glob, Grep, Task, Bash(git *), Bash(ls *), Bash(treefmt*), Bash(pnpm eslint*), Bash(pnpm tsc*), Bash(pnpm jest*), Bash(pnpm e2e:test*) |
ãã¹ã远å ã»å®è¡ã»ä¿®æ£ãªãŒã±ã¹ãã¬ãŒã¿ãŒ
ãã®ã¹ãã«ã¯ã³ãŒãããŒã¹ã®ãã¹ãã«ãã¬ããžãåæããäžè¶³ããŠãããã¹ãïŒJeståäœãã¹ã + Detox E2Eãã¹ãïŒãworkerãµããšãŒãžã§ã³ãã§äžŠåäœæããå
šãã¹ããéãããšã確èªããŸãã
å®è¡ãããŒ
Step 1: ã¹ã³ãŒãã®æ±ºå®
$ARGUMENTS ã®è§£é:
- ãã¹ïŒäŸ:
src/features/alarmïŒâ ãã®ãã£ã¬ã¯ããªã«éå®ããŠã«ãã¬ããžåæ
- ãã¡ã€ã«ãã¹ïŒäŸ:
src/core/time/conversions.tsïŒâ ãã®ãã¡ã€ã«ã®ãã¹ããäœæ/æ¹å
- ããŒã¯ãŒãïŒäŸ:
atoms, hooks, modelsïŒâ 該åœã«ããŽãªã®ãã¹ãã«éå®
- 空 â ã³ãŒãããŒã¹å
šäœãåæããæãåªå
床ã®é«ãæªãã¹ãé åãç¹å®
Step 2: ã«ãã¬ããžåæïŒãµããšãŒãžã§ã³ãïŒ
ãµããšãŒãžã§ã³ããèµ·åããŠãåäœãã¹ã + E2Eãã¹ã ã®ã«ãã¬ããžã®ã£ãããåæããã
ãšãŒãžã§ã³ãéžæ: é垞㯠Explore ãµããšãŒãžã§ã³ãã§ååãæ¢åã³ãŒãã®ãã¹ããã¿ãŒã³ãæ·±ãåæããå¿
èŠãããå ŽåïŒè€éãªã¢ãã¯æ§æãçµ±åãã¹ãèšèšçïŒã¯ feature-dev ã® code-explorer ãæå¹ã
Task tool:
subagent_type: "Explore"
description: "Analyze test coverage gaps"
prompt: |
ãããžã§ã¯ãã®ãã¹ãã«ãã¬ããžãåæããŠãã ããã
<Step 1ã§æ±ºå®ããã¹ã³ãŒã>
1. `src/` 以äžã®ãœãŒã¹ãã¡ã€ã«ãåæ
2. `__tests__/` 以äžã®å¯Ÿå¿ãããã¹ããã¡ã€ã«ã®æç¡ã確èª
3. ãã¹ãããããã¡ã€ã«ã«ã€ããŠããã¹ãå
容ãèªã¿ãã«ãã¬ããžã®ååããè©äŸ¡
4. ãã¹ãããªããã¡ã€ã«ãåªå
床ä»ãã§ãªã¹ãã¢ãã
1. ããžãã¹ããžãã¯ïŒcore/, models/, atoms/ïŒâ ãã°ã®åœ±é¿ã倧ãã
2. ã«ã¹ã¿ã ããã¯ïŒhooks/ïŒâ è€æ°ç»é¢ã§å
±æããã
3. ãµãŒãã¹ã»ãŠãŒãã£ãªãã£ïŒservices/, strategies/ïŒâ åäœãã¹ããããã
4. ç»é¢ã³ã³ããŒãã³ãïŒscreens/ïŒâ çµ±åãã¹ããšããŠäŸ¡å€ããã
5. UIã³ã³ããŒãã³ãïŒcomponents/ïŒâ ã¹ãããã·ã§ãã or ã€ã³ã¿ã©ã¯ã·ã§ã³ãã¹ã
1. `__tests__/e2e/` 以äžã®æ¢åE2Eãã¹ããã¡ã€ã«ãåæããã«ããŒããŠãããããŒãææ¡
2. `src/screens/` ã®åç»é¢ãšæ©èœãåæããE2Eãã¹ãã®æç¡ã確èª
3. 以äžã®èгç¹ã§æªã«ããŒã®ãŠãŒã¶ãŒãããŒãç¹å®:
- æ°èŠè¿œå ãããç»é¢ã»æ©èœã«E2Eãã¹ãããªãã
- æ¢åE2Eãã¹ãã§ã«ããŒãããŠããªãéèŠãªãŠãŒã¶ãŒæäœãããã
- ãšããžã±ãŒã¹ïŒç©ºç¶æ
ããšã©ãŒè¡šç€ºãã¹ã¯ããŒã«ããã€ã¢ãã°çïŒã®ãã¹ãäžè¶³
4. åç»é¢ã®testIDã®æç¡ã確èªïŒE2Eãã¹ãã«å¿
èŠïŒ
1. ãŠãŒã¶ãŒã®ã³ã¢ãããŒïŒsetup, alarm CRUD, timeræäœïŒâ ååž°ãªã¹ã¯ãé«ã
2. ç»é¢é·ç§»ã»ããã²ãŒã·ã§ã³ â ã«ãŒãã£ã³ã°ã®ãã°ãé²ã
3. èšå®å€æŽãšæ°žç¶å â èšå®ãæ£ããä¿åã»åæ ãããã
4. ãšã©ãŒãã³ããªã³ã°ã»å¢çå€ â ãã€ã¢ãã°ãç©ºç¶æ
ãå
¥åããªããŒã·ã§ã³
åãã¡ã€ã«ã«ã€ããŠ:
- ãã¡ã€ã«ãã¹
- ãã¹ãã®æç¡ïŒæã®å Žåã¯ã«ãã¬ããžè©äŸ¡ïŒ
- æšå¥šãããã¹ãå
容ïŒäœããã¹ããã¹ããïŒ
- åªå
床ïŒé«/äž/äœïŒ
- ãã¹ãäœæã®é£æåºŠïŒç°¡å/æ®é/è€éïŒ
- å¿
èŠãªã¢ãã¯ïŒAsyncStorage, Notifee, NavigationçïŒ
åãŠãŒã¶ãŒãããŒã«ã€ããŠ:
- ãããŒåïŒäŸ: "Desk Clock toggle"ïŒ
- 察å¿ç»é¢/æ©èœ
- æ¢åE2Eãã¹ãã®æç¡ïŒæã®å Žåã¯äžè¶³ã·ããªãªïŒ
- æšå¥šãããã¹ãã·ããªãª
- åªå
床ïŒé«/äž/äœïŒ
- testID远å ãå¿
èŠãªã³ã³ããŒãã³ãïŒããã°ïŒ
Step 3: ãã¹ãã¿ã¹ã¯åè§£
Explore ã®åæçµæãå
ã«ãåäœãã¹ãã¿ã¹ã¯ ãš E2Eãã¹ãã¿ã¹ã¯ ãåè§£ããã
3a: åäœãã¹ãã¿ã¹ã¯
åè§£ã®åå:
- 1ã¿ã¹ã¯ = é¢é£ãã1-3ãã¡ã€ã«ã®ãã¹ã â åäžã¢ãžã¥ãŒã«å
ã®ãã¡ã€ã«ã¯ãŸãšããŠãã
- åã¿ã¹ã¯ã¯ä»ã®ã¿ã¹ã¯ã«äŸåããªãïŒãã¹ããã¡ã€ã«ãéè€ããªãïŒããš
- æ¢åãã¹ãã®æ¹åãšæ°èŠãã¹ãäœæãåºå¥ãã
- ã¢ãã¯ãã¿ãŒã³ãå
±éã®ãã¡ã€ã«ã¯ãŸãšãããšå¹çç
åã¿ã¹ã¯ã«å¯ŸããŠä»¥äžãå®çŸ©:
- ã¿ã¹ã¯å: çã説æïŒäŸ:
test-alarm-atomsïŒ
- çš®å¥:
unit
- 察象ãã¡ã€ã«: ãã¹ã察象ã®ãœãŒã¹ãã¡ã€ã«ãã¹
- ãã¹ããã¡ã€ã«: äœæãããã¹ããã¡ã€ã«ãã¹ïŒ
__tests__/ é
äžãsrcæ§é ããã©ãŒïŒ
- ãã¹ãå
容: äœããã¹ããããïŒé¢æ°åãã·ããªãªããšããžã±ãŒã¹ïŒ
- åç
§ãã¹ã: ãã¿ãŒã³ãèžè¥²ãã¹ãæ¢åãã¹ããã¡ã€ã«
- å¿
èŠãªã¢ãã¯: ã¢ãã¯ãã¹ãã¢ãžã¥ãŒã«
- åãå
¥ãåºæº: ãã¹ããéãããš + ã«ãã¬ããžèŠä»¶
3b: E2Eãã¹ãã¿ã¹ã¯
åè§£ã®åå:
- 1ã¿ã¹ã¯ = 1ã€ã®ç»é¢ or 1ã€ã®ãŠãŒã¶ãŒãã㌠â E2Eãã¹ãã¯æ©èœãããŒåäœã§åå²
- æ°èŠE2Eãã¹ããã¡ã€ã«ã®å Žåã¯
__tests__/e2e/<feature>.e2e.ts ã«é
眮
- æ¢åE2Eãã¹ããã¡ã€ã«ãžã®ã·ããªãªè¿œå ã1ã¿ã¹ã¯ãšããŠæ±ã
- testID远å ãå¿
èŠãªå ŽåããœãŒã¹ã³ãŒãã®å€æŽãã¿ã¹ã¯ã«å«ãã
åã¿ã¹ã¯ã«å¯ŸããŠä»¥äžãå®çŸ©:
- ã¿ã¹ã¯å: çã説æïŒäŸ:
e2e-deskclock-toggleïŒ
- çš®å¥:
e2e
- 察象ç»é¢/æ©èœ: ãã¹ã察象ã®ç»é¢ã»ãããŒ
- ãã¹ããã¡ã€ã«: äœæ/ç·šéããE2Eãã¹ããã¡ã€ã«ãã¹ïŒ
__tests__/e2e/ é
äžïŒ
- ãã¹ãã·ããªãª: ãã¹ããããŠãŒã¶ãŒæäœãããŒïŒã¹ããããã€ã¹ãããïŒ
- åç
§ãã¹ã: ãã¿ãŒã³ãèžè¥²ãã¹ãæ¢åE2Eãã¹ããã¡ã€ã«
- testID远å : ãœãŒã¹ã³ãŒãã«è¿œå ãå¿
èŠãªtestIDã®äžèЧïŒãã¡ã€ã«ãã¹ + ã³ã³ããŒãã³ã + testIDåïŒ
- åææ¡ä»¶: ãã¹ãå®è¡åã«å¿
èŠãªç¶æ
ïŒsetupå®äºãç¹å®ã®ç»é¢é·ç§»çïŒ
- åãå
¥ãåºæº:
pnpm e2e:test ã§éãããšïŒãšãã¥ã¬ãŒã¿èµ·åãå¿
èŠïŒ
Step 4: ãŠãŒã¶ãŒç¢ºèª
ã¿ã¹ã¯åè§£çµæããŠãŒã¶ãŒã«æç€ºããæ¿èªãåŸãã以äžã衚瀺:
- åäœãã¹ã ã¿ã¹ã¯äžèЧïŒå¯Ÿè±¡ãã¡ã€ã« + ãã¹ãæŠèŠïŒ
- E2Eãã¹ã ã¿ã¹ã¯äžèЧïŒå¯Ÿè±¡ãã㌠+ ãã¹ãã·ããªãªæŠèŠïŒ
- æ°èŠãã¹ããã¡ã€ã«æ°ïŒåäœ + E2EïŒ
- 䞊åå®è¡ã°ã«ãŒã
- æšå®workeræ°
- E2Eãã¹ãå®è¡ã«ã¯ãšãã¥ã¬ãŒã¿ãå¿
èŠãªæš
ãŠãŒã¶ãŒãæ¿èªãããæ¬¡ã®ã¹ããããžãä¿®æ£èŠæãããã°èšç»ã調æŽã
Step 5: ã¯ãŒã«ãŒäžŠåèµ·å
ãŸããåã¿ã¹ã¯çšã® worktree ã .worktree/ 以äžã«äœæãã:
git worktree add ".worktree/<task-name>" -b "claude/<conventional-prefix>/<task-name>"
次ã«ã1ã€ã®ã¡ãã»ãŒãžå
ã§ è€æ°ã® Task tool åŒã³åºããè¡ããã¯ãŒã«ãŒã䞊åèµ·åããã
åã¯ãŒã«ãŒã®èµ·åãã©ã¡ãŒã¿:
Task tool:
subagent_type: "worker"
description: "<ã¿ã¹ã¯åïŒ3-5èªïŒ>"
max_turns: 100
prompt: |
## äœæ¥ãã£ã¬ã¯ããª
`.worktree/<task-name>`ïŒãã®ãã£ã¬ã¯ããªå
ã§ã®ã¿äœæ¥ããããšïŒ
<ãœãŒã¹ãã¡ã€ã«ãã¹ã®ãªã¹ã>
<ãã¹ããã¡ã€ã«ãã¹ã®ãªã¹ãïŒ__tests__/ é
äžïŒ>
<å
·äœçãªãã¹ãã±ãŒã¹ã®èª¬æ>
- describe/itã®æ§é
- ãã¹ããã¹ãã·ããªãªïŒæ£åžžç³»ãç°åžžç³»ããšããžã±ãŒã¹ïŒ
- æåŸ
ãããæ¯ãèã
<æ¢åãã¹ããã¡ã€ã«ãã¹ â ã¢ãã¯æ¹æ³ããã¹ãã¹ã¿ã€ã«ã®åèãããããäœãåèã«ãã¹ãããæ³šèš>
<å¿
èŠãªã¢ãã¯äžèЧãšèšå®æ¹æ³ãå
·äœçã«èšè¿°>
- [ ] ãã¹ããå
šãŠãã¹ããããš (`pnpm jest <ãã¹ããã¡ã€ã«ãã¹>`)
- [ ] ãã¹ã察象ã®äž»èŠãªããžãã¯/åå²ãã«ããŒãããŠããããš
- [ ] æ¢åãã¹ãã®ãã¿ãŒã³ã«æºæ ããŠããããš
- [ ] treefmt + eslint + tsc ããã¹ããããš
以äžã®ãã©ãŒãããã®ã¿è¿ããã³ãã³ãåºåãã³ãŒãå
šæã¯å«ããªã:
- **çµæ**: æå / 倱æïŒå€±æçç±ïŒ
- **äœæãã¡ã€ã«**: ãã¹ã®ç®æ¡æžã
- **ãã¹ãæ°**: X passed, Y failed, Z skipped
- **ã³ããã**: ããã·ã¥ïŒ1è¡ïŒ
model: "opus"
E2Eãã¹ãã¯ãŒã«ãŒã®ããã³ãããã³ãã¬ãŒã
Task tool:
subagent_type: "worker"
description: "<ã¿ã¹ã¯åïŒ3-5èªïŒ>"
max_turns: 100
prompt: |
## äœæ¥ãã£ã¬ã¯ããª
`.worktree/<task-name>`ïŒãã®ãã£ã¬ã¯ããªå
ã§ã®ã¿äœæ¥ããããšïŒ
<ç»é¢åã»æ©èœåãšãŠãŒã¶ãŒãããŒã®æŠèŠ>
`__tests__/e2e/<feature>.e2e.ts`
<ã¹ããããã€ã¹ãããã®ãŠãŒã¶ãŒæäœãããŒ>
- describe/itã®æ§é
- åitã§æ€èšŒãããŠãŒã¶ãŒæäœãšæåŸ
çµæ
- åææ¡ä»¶ïŒsetupå®äºãç»é¢é·ç§»çïŒ
<ãã¡ã€ã«ãã¹ + ã³ã³ããŒãã³ã + 远å ããtestIDå>
â» E2Eãã¹ãã§ã¯ `by.id()` ã§testIDãåç
§ããããã
ãœãŒã¹ã³ã³ããŒãã³ãã« `testID` propãèšå®ãããŠããå¿
èŠããã
- `__tests__/e2e/utils/helpers.ts` â ãã«ããŒé¢æ°ïŒlaunchAppFresh, completeSetup, navigateToTab, waitVisibleïŒ
- <æ¢åE2Eãã¹ããã¡ã€ã«ãã¹ â é¡äŒŒãããŒã®ãã¹ããã¿ãŒã³åè>
- Detox v20 API ã䜿çš: `import { device, element, by, expect, waitFor } from "detox"`
- ãã«ããŒé¢æ°ã掻çš: `import { launchAppFresh, completeSetup, navigateToTab, waitVisible } from "./utils/helpers"`
- èŠçŽ éžæ: `by.id(testID)` ãåªå
ãããã¹ã㯠`by.text()`, ã¢ã¯ã»ã·ããªãã£ã¯ `by.label()`
- MD3 BottomNavigation ã®ã©ãã«éè€: `element(by.text("Label")).atIndex(0).tap()`
- Switch ã³ã³ããŒãã³ã: `by.type("com.facebook.react.views.switchview.ReactSwitch")`
- åŸ
æ©: `waitFor(element(...)).toBeVisible().withTimeout(5000)`
- ããŒããŒã dismissal: å
¥ååŸã« `await device.pressBack()`
- ã¹ã¯ããŒã«: `element(by.id("scroll-container")).scrollTo("bottom")`
- å
šæäœã¯ `async/await` å¿
é
- ãã¹ããã¡ã€ã«å: `<feature>.e2e.ts`ïŒ.test.ts ã§ã¯ãªãïŒ
- [ ] ãã¹ããã¡ã€ã«ãæ£ããå Žæã«é
眮ãããŠããããš
- [ ] æ¢åE2Eãã¹ãã®ãã¿ãŒã³ã«æºæ ããŠããããš
- [ ] testID远å ãå¿
èŠãªå ŽåããœãŒã¹ã³ãŒããä¿®æ£ããŠããããš
- [ ] treefmt + eslint + tsc ããã¹ããããš
- [ ] `pnpm jest __tests__/e2e/<feature>.e2e.ts` ã§æ§æãšã©ãŒããªãããšïŒDetoxå®è¡ã¯äžèŠïŒ
以äžã®ãã©ãŒãããã®ã¿è¿ããã³ãã³ãåºåãã³ãŒãå
šæã¯å«ããªã:
- **çµæ**: æå / 倱æïŒå€±æçç±ïŒ
- **äœæ/倿Žãã¡ã€ã«**: ãã¹ã®ç®æ¡æžã
- **ãã¹ãæ°**: X passed, Y failed, Z skipped
- **ã³ããã**: ããã·ã¥ïŒ1è¡ïŒ
model: "opus"
éèŠ: ãªãŒã±ã¹ãã¬ãŒã¿ãŒã¯åã»ã¯ã·ã§ã³ãå
·äœçã«åããããšãç¹ã«ãåç
§ãã¹ããã«ã¯ã¢ãã¯ãã¿ãŒã³ã®åèå
ããããã¹ãå
容ã/ããã¹ãã·ããªãªãã«ã¯å
·äœçãªãã¹ãã±ãŒã¹åãåæããããšã
Step 6: çµæåéãšãªããŒã¹ïŒãããå®è¡ïŒ
å
šã¯ãŒã«ãŒã®å®äºåŸ:
- åã¯ãŒã«ãŒã®çµæãåéïŒæå/倱æãäœæãã¹ãæ°ããã¹ãçµæïŒ
- 倱æããã¿ã¹ã¯ãããã°åå ãåæãããªãã©ã€ãŸãã¯ãŠãŒã¶ãŒã«å ±å
- æåããã¿ã¹ã¯ã®ã¿ ãªããŒã¹å¯Ÿè±¡ãšãã
ã³ã³ããã¹ãç¯çŽã®ãããå
šãã©ã³ãã®ãªããŒã¹ã»ã¯ãªãŒã³ã¢ããã1ã€ã® bash ã«ãŸãšãã:
BRANCHES=("claude/test/task-a:task-a" "claude/test/task-b:task-b")
for entry in "${BRANCHES[@]}"; do
branch="${entry%%:*}"; task="${entry##*:}"
echo "=== Rebasing $branch ==="
if ! git rebase "$branch"; then
echo "CONFLICT in $branch"; git rebase --abort; exit 1
fi
git worktree remove ".worktree/$task" 2>/dev/null
git branch -D "$branch"
done && echo "ALL REBASES OK"
ã³ã³ããªã¯ãçºçæ: ã¹ã¯ãªããã忢ããã®ã§ãŠãŒã¶ãŒã«å ±åãæç€ºãåŸ
ã€ãèªå解決ããªãããšã
Step 7: çµ±åæ€èšŒïŒproject-reviewerïŒ
7a. èªåä¿®æ£å¯èœãªåé¡ãå
ã«è§£æ¶
treefmt && pnpm eslint . --fix && pnpm eslint . && pnpm tsc --noEmit
ãšã©ãŒãããã°æåä¿®æ£ããèªåä¿®æ£ã§å€æŽãããã°ã³ãããããã
7b. project-reviewer ã§æçµæ€èšŒ
Task tool:
subagent_type: "project-reviewer"
description: "Post-merge test validation"
prompt: |
ãã¹ããã©ã³ãã®ãªããŒã¹åŸã®çµ±åç¶æ
ãæ€èšŒããŠãã ããã
å
šãã§ãã¯ïŒtsc, eslint, treefmt, jest, nix flake checkïŒãå®è¡ããçµæãå ±åããŠãã ããã
- ALL CHECKS PASSED â Step 8 ãžé²ã
- FAIL ãã â ãªãŒã±ã¹ãã¬ãŒã¿ãŒãåé¡ãä¿®æ£ããå床 reviewer ã§ç¢ºèª
åäœãã¹ã倱ææ: ãšã©ãŒã¡ãã»ãŒãžãåæãããã¹ãã³ãŒãã®åé¡ã¯ä¿®æ£ããããã¯ã·ã§ã³ã³ãŒãã®åé¡ã¯ãŠãŒã¶ãŒã«å ±åãä¿®æ£åŸ reviewer ã§å確èªã
E2Eãã¹ã: ãšãã¥ã¬ãŒã¿æªèµ·åæã¯åãã§ãã¯ã®ã¿ãæåå®è¡ãäŸé Œããã
Step 8: æçµå ±å
å
šã²ãŒãééåŸããŠãŒã¶ãŒã«æçµçµæãå ±å:
- äœæãããã¹ããã¡ã€ã«äžèЧïŒåäœãã¹ã + E2Eãã¹ãïŒ
- åäœãã¹ãæ°ïŒpass/fail/skipïŒã®å€åïŒBefore â AfterïŒ
- E2Eãã¹ãæ°ã®å€åïŒBefore â AfterïŒ
- ãœãŒã¹ã³ãŒããžã®testID远å äžèЧïŒE2Eãã¹ãçšã«è¿œå ããå ŽåïŒ
- çºèŠããããã°ãåé¡ç¹ïŒããã°ïŒ
- ã«ãã¬ããžã®æ¹åç¶æ³
- æ®åããæªãã¹ãé åïŒããã°ïŒ
- E2Eãã¹ãæªå®è¡ã®å Žåã¯ãã®æšãšæåå®è¡ã³ãã³ãïŒ
pnpm e2e:testïŒ
ãã¹ãäœæã¬ã€ãã©ã€ã³
workerã«æž¡ãããã³ããã«ã¯ã以äžã®ã¬ã€ãã©ã€ã³ãå«ããããš:
äžè¬åå
@testing-library/react-native v13 ãäœ¿çš â å
šAPIãasyncïŒawait render(), await fireEvent.press() çïŒ
- ãã¹ããã¡ã€ã«ã¯
__tests__/ 以äžã« src/ ã®ãã£ã¬ã¯ããªæ§é ããã©ãŒããŠé
眮
- ãã¡ã€ã«åã¯
<察象ãã¡ã€ã«å>.test.ts or .test.tsx
describe ã§ã¢ãžã¥ãŒã«/颿°åãã°ã«ãŒãã³ã°ãit ã§å
·äœçãªæ¯ãèããèšè¿°
ã¢ãã¯ãã¿ãŒã³ïŒæ¢åãã¹ãããèžè¥²ïŒ
jest.mock("@react-native-async-storage/async-storage", () => ({
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
}));
jest.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => key,
i18n: { language: "en", changeLanguage: jest.fn() },
}),
}));
jest.mock("@notifee/react-native", () => ({
createChannel: jest.fn(),
displayNotification: jest.fn(),
cancelNotification: jest.fn(),
}));
jest.mock("react-native-paper", () => {
const actual = jest.requireActual("react-native-paper");
return { ...actual };
});
ã³ã³ããŒãã³ããã¹ãã®Providerèšå®
import { Provider as JotaiProvider, createStore } from "jotai";
import { PaperProvider } from "react-native-paper";
const renderWithProviders = (ui: React.ReactElement) => {
const store = createStore();
return render(
<JotaiProvider store={store}>
<PaperProvider>{ui}</PaperProvider>
</JotaiProvider>,
);
};
E2Eãã¹ãäœæã¬ã€ãã©ã€ã³
workerã«æž¡ãããã³ããã«ã¯ã以äžã®E2Eã¬ã€ãã©ã€ã³ãå«ããããš:
ãã¬ãŒã ã¯ãŒã¯
- Detox v20 ãäœ¿çš â React Nativeåãã°ã¬ãŒããã¯ã¹E2Eãã¹ããã¬ãŒã ã¯ãŒã¯
- ãã¹ããã¡ã€ã«ã¯
__tests__/e2e/ é
äžã« <feature>.e2e.ts ãšããŠé
眮
- èšå®:
.detoxrc.js + __tests__/e2e/jest.config.js
ã€ã³ããŒããã¿ãŒã³
import { device, element, by, expect, waitFor } from "detox";
import {
launchAppFresh,
launchApp,
completeSetup,
navigateToTab,
waitVisible,
} from "./utils/helpers";
ãã«ããŒé¢æ°
| 颿° | çšé |
|---|
launchAppFresh() | AsyncStorage ã¯ãªã¢ + æ°èŠèµ·å |
launchApp() | æ¢åç¶æ
ãä¿æããŠèµ·å |
completeSetup(hours, minutes) | ã»ããã¢ããç»é¢ãå®äº |
navigateToTab(label) | BottomTab ã«é·ç§»ïŒ.atIndex(0) å¯Ÿå¿æžã¿ïŒ |
waitVisible(testID, timeout) | èŠçŽ ã®è¡šç€ºãåŸ
æ© |
èŠçŽ éžæã®åªå
é äœ
by.id(testID) â æãå®å®ãã³ã³ããŒãã³ãã« testID prop ãèšå®
by.text("衚瀺ããã¹ã") â åçã§ãªãããã¹ãã«äœ¿çš
by.label("Accessibility Label") â ã¢ã¯ã»ã·ããªãã£ã©ãã«
by.type("NativeClassName") â ãã€ãã£ãã³ã³ããŒãã³ãïŒæçµææ®µïŒ
ãã䜿ããã¿ãŒã³
await element(by.text("Alarm")).atIndex(0).tap();
const toggle = element(
by.type("com.facebook.react.views.switchview.ReactSwitch"),
).atIndex(0);
await toggle.tap();
await waitFor(element(by.id("dialog")))
.toBeVisible()
.withTimeout(5000);
await waitFor(element(by.id("dialog")))
.not.toBeVisible()
.withTimeout(5000);
await element(by.id("scroll-view")).scrollTo("bottom");
await device.pressBack();
await element(by.id("input")).clearText();
await element(by.id("input")).typeText("value");
ãã¹ãæ§é ã®ãã¹ããã©ã¯ãã£ã¹
beforeAll ã§ã¢ããªèµ·å + ã»ããã¢ããå®äº + 察象ç»é¢ãžã®é·ç§»
describe ã§æ©èœã°ã«ãŒããit ã§åå¥ã®æäœã»æ€èšŒ
- ãã¹ãã¯é åºäŸåïŒåã®ãã¹ãã®ç¶æ
ãåŒãç¶ãïŒã蚱容ãããïŒE2Eãã¹ãã®ç¹æ§äžïŒ
- ã¿ã€ã ã¢ãŠãã¯
waitFor(...).withTimeout(ms) ã§æç€ºçã«èšå®
testID 远å ã®ã«ãŒã«
E2Eãã¹ãã§ by.id() ã䜿ãããã«ããœãŒã¹ã³ã³ããŒãã³ãã« testID ã远å ããå Žå:
- åœåèŠå:
kebab-caseïŒäŸ: alarm-edit-screen, save-button, cycle-hours-inputïŒ
- ç»é¢ã®ã«ãŒãã³ã³ããŒãã³ãã«ã¯
<feature>-screenïŒäŸ: clock-screen, settings-screenïŒ
- ãã¿ã³ã«ã¯
<action>-buttonïŒäŸ: save-button, delete-buttonïŒ
- å
¥åãã£ãŒã«ãã«ã¯
<field>-inputïŒäŸ: label-input, hours-inputïŒ
- æ¢åã®
testID ãšéè€ããªãããš
泚æäºé
- workerãµããšãŒãžã§ã³ãã¯ãµããšãŒãžã§ã³ããåŒã¹ãªãïŒãã¹ãäžå¯ïŒ
- åworkerã¯ç¬ç«ããworktreeã§åäœããããããã¹ããã¡ã€ã«ã®ç«¶åã¯çºçããªã
- åäœãã¹ãworkerã¯ãã¹ã察象ã®ãããã¯ã·ã§ã³ã³ãŒãã倿ŽããªãïŒãã¹ãã³ãŒãã®ã¿äœæïŒ
- E2Eãã¹ãworkerã¯testid远å ã®ããã«ãœãŒã¹ã³ãŒãã倿ŽããŠããïŒãã ãtestID prop远å ã®ã¿ãããžãã¯å€æŽã¯äžå¯ïŒ
- æ¢åãã¹ããç Žå£ããªãããš â ããŒãžåŸã«æ¢åãã¹ããå«ããŠå
šä»¶ééã確èªãã
- 倧éã®ã¿ã¹ã¯ïŒ5+ïŒãããå Žåã2-3ã®ãããã«åããŠé 次å®è¡ããããšãæšå¥š
- E2Eãã¹ãã®å®è¡ïŒ
pnpm e2e:testïŒã«ã¯ãšãã¥ã¬ãŒã¿ + ãã«ãæžã¿APKãå¿
èŠããšãã¥ã¬ãŒã¿ãèµ·åããŠããªãå Žåã¯åãã§ãã¯ã®ã¿ã§æ€èšŒããæåå®è¡ãäŸé Œãã
- E2Eãã¹ããã¡ã€ã«ã®ç«¶åã«æ³šæ: åäžã®
.e2e.ts ãã¡ã€ã«ãè€æ°workerãç·šéããªãããã«ã¿ã¹ã¯ãåè§£ããããš