Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/e2e-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ jobs:
echo "=== Available AVDs ==="
emulator -list-avds 2>/dev/null || [ -z "${ANDROID_HOME:-}" ] || "$ANDROID_HOME/emulator/emulator" -list-avds 2>/dev/null || true

echo "=== Emulator diagnostics ==="
echo "--- wm size ---"
adb shell wm size || true
echo "--- wm density ---"
adb shell wm density || true
echo "--- display info (dumpsys display | grep -E 'mBaseDisplayInfo|density|DisplayDeviceInfo') ---"
adb shell dumpsys display | grep -E 'mBaseDisplayInfo|density|DisplayDeviceInfo' || true
echo "--- top inset (statusBars / captionBar) ---"
adb shell dumpsys window displays | grep -E 'type=(statusBars|captionBar|navigationBars)' || true
echo "--- window policy (freeform / desktop) ---"
adb shell settings get global force_desktop_mode_on_external_displays 2>/dev/null || true
adb shell settings get global enable_freeform_support 2>/dev/null || true
echo "=== End diagnostics ==="

# APK 설치 (runner가 줄마다 별도 sh -c로 실행해 변수 미공유 → 경로 리터럴 사용)
ls -la examples/demo-app/android/app/build/outputs/apk/release/app-release.apk
adb install -r examples/demo-app/android/app/build/outputs/apk/release/app-release.apk
Expand Down
29 changes: 28 additions & 1 deletion docs/e2e-ci-reliability.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ GitHub Actions에서 E2E 테스트가 가끔 실패하는 경우(flakiness)와

---

## 간헐 실패의 공통·플랫폼별 원인

| 구분 | 원인 요약 |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **공통** | CI 러너는 로컬보다 **시뮬/에뮬·앱·MCP·idb/adb** 전반이 느리다. 같은 스텝이라도 타이밍에 따라 query_selector·measure·tap 중 하나가 지연되거나 실패한다. |
| **iOS** | **idb `ui tap`** 이 시뮬레이터에 전달되기 전/후로 시뮬레이터가 무거우면 25초 타임아웃까지 응답이 안 나온다. `query_selector`는 성공해도 그 다음 tap 단계에서 "Command timed out"이 난다. |
| **Android** | **measure 보충**이 실패한다. 요소는 셀렉터로 찾지만, Fabric/Bridge의 `measureInWindow`·`UIManager.measure` 콜백이 CI에서 늦게 오거나 오지 않아 "has no measure data"가 난다. 화면 전환·리플로우 직후에 measure를 요청할 때 특히 불안정하다. |

즉, **iOS는 “tap 명령 타임아웃”**, **Android는 “요소는 찾았지만 좌표(measure) 수집 실패”**가 주된 간헐 원인이다. 둘 다 **CI 환경의 지연·타이밍**에서 오는 flakiness다.

---

## CI에서 하는 검사

- **Doctor**: E2E 워크플로에서 의존성 설치 후 `doctor`를 실행한다. Node ≥ 24, react-native ≥ 0.74
Expand All @@ -27,4 +39,19 @@ GitHub Actions에서 E2E 테스트가 가끔 실패하는 경우(flakiness)와
## 재실행 권장

일시적 지연이나 측정 타이밍 이슈로 실패한 경우, GitHub Actions에서 **Re-run failed jobs** 또는 **Re-run all jobs**로
한 번 더 돌리면 통과하는 경우가 많다. 스위트 전체 재시도는 아직 CI에 넣지 않았으며, 필요 시 수동 재실행을 권장한다.
한 번 더 돌리면 통과하는 경우가 많다.

---

## 항상 성공에 가깝게 하려면

간헐 원인이 타이밍/지연이므로, **한 번 실패해도 한 번 더 시도**하면 통과할 가능성이 높다. 아래는 효과 순으로 정리한 선택지다.

| 우선순위 | 방법 | 설명 | 부작용 |
| -------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **1** | **CI에서 E2E 단계 재시도** | E2E YAML 테스트 실행 스텝을 실패 시 1회 재시도(예: 최대 2회 실행). 한 번의 flake를 흡수한다. | 실패 시 런이 2배 길어질 수 있음(보통 첫 시도에서 성공). |
| **2** | **Runner에서 스텝 재시도** | `runner.ts`에서 `executeStep` 실패 시 짧은 대기 후 같은 스텝을 1회 더 실행. tap/measure 타임아웃을 스텝 단위로 완화. | 코드 변경 필요, 모든 스텝에 재시도 적용됨. |
| **3** | **tap 전 measure 재조회** | `has no measure data` 시 클라이언트에서 300ms 대기 후 `query_selector` 한 번 더 호출해 measure 보충 후 tap. Android measure flake 완화. | 서버/클라이언트 코드 변경. |
| **4** | **YAML 보수적 작성** | tap 직전 `wait`·`waitForVisible`·`waitForText`로 상태 안정화. testID 있으면 선택자로 testID 우선 사용. | 스텝 수·실행 시간 증가. (이미 일부 적용됨) |

**권장**: **1번(CI E2E 단계 재시도)**를 먼저 적용하는 것이 부담이 적고 효과가 크다. 워크플로에서 "E2E YAML 테스트 실행" 스텝을 `run`으로 감싸서 실패 시 한 번 더 실행하거나, [nick-fields/retry](https://github.com/nick-fields/retry) 같은 액션으로 감싸면 된다. 2·3번은 필요 시 추가로 검토하면 된다.
5 changes: 3 additions & 2 deletions examples/demo-app/e2e/all-steps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ setup:
- waitForVisible:
selector: 'Pressable:text("Count:")'
timeout: 25000
- wait: 500

steps:
# ─── Step 1: Press Counter (텍스트로 찾기) ─────────────────────────
- assertText: { text: 'Count: 0', selector: 'Pressable:text("Count:")' }
# ─── Step 1: Press Counter (CI 초기 렌더 안정화: waitForText로 초기 상태 대기)
- waitForText: { text: 'Count: 0', selector: 'Pressable:text("Count:")', timeout: 5000 }
- tap: { selector: 'Pressable:text("Count:")' }
- wait: 500
- waitForText: { text: 'Count: 1', selector: 'Pressable:text("Count:")', timeout: 6000 }
Expand Down
Loading
Loading