docs(mypage): 공지 롤링 리뷰와 QA 증거를 보존한다
@@ -0,0 +1,86 @@
|
||||
# 마이 탭 공지사항 Native QA 증거
|
||||
|
||||
**판정: NEEDS WORK. 비활성 중 롤링 중지 및 활성 복귀 후 재개(S5)가 실패했다. 제품 코드는 수정하지 않았다.**
|
||||
|
||||
## 실행 범위와 출처
|
||||
|
||||
이 증거는 전체 SodaLive 앱이 아니라 실제 `UpdateBannerView`를 실행한 **native UIKit + SwiftUI UIHostingController 하네스**다. 로그인·서버 요청·실사용자 데이터는 사용하지 않았다. 하네스 및 빌드 캐시는 작업 전용 TMP에만 생성했고 제품 프로젝트에 추가하지 않았다.
|
||||
|
||||
- 환경: Xcode 26.0 / SDK 26.0, Intel `x86_64`, iPhone SE 3세대 시뮬레이터, iOS 18.3 런타임, 375pt 폭, PNG 750×1334px.
|
||||
- 소유 device: `B929E0F0-50F7-46C1-988E-95272D0CE7A2`, bundle: `local.sodalive.NoticeNativeQA`.
|
||||
- 복수 구현 존재 확인 후 관련 제품 파일이 15초간 동일한 것을 확인했다. 마지막 네이티브 실행 종료 후에도 모든 제품 입력 해시가 동일했다. [provenance.json](provenance.json) 참고.
|
||||
- `MyPageView.swift` SHA-256: `86a3dee7a4f2ce5bfa99624ff08c0b8eb7c1e88556fc5c04458f7ed2f72c7638`.
|
||||
- 원본 `UpdateBannerView` 블록 SHA-256: `828d5737650436db9a69fd98efc3475362d7045285d2fb8dbdd17870d57047a6`. [추출본](source/UpdateBannerView.swift)은 블록을 변경하지 않고 `import SwiftUI`만 앞에 추가했다.
|
||||
|
||||
`MyPageNoticeRoller`, `NoticeItem` 포함 응답 파일, `FontModifier`, `ColorExtension`, `SodaSpacing`도 실제 소스를 컴파일했다. 실제 `Pretendard-Regular.otf`를 등록했다. `AppState`/`AppStep`은 title/content/date 수집 sink로, `LanguageOption`은 폰트 선택에 필요한 enum case로 대체했다. 마이 페이지 주변부만 fixture·mount·phase 드라이버 및 QA 설명으로 대체했다. 빈 배열의 부모 조건도 현재 제품의 `!latestNotices.isEmpty`와 동일하게 구성했다.
|
||||
|
||||
`PhaseProbe`는 배너 소스를 변경하지 않는 투명한 sibling이며 같은 환경의 실제 `scenePhase` 변경을 기록한다. 실제 OS 복귀 검사는 UIKit notification을 환경에 전달하는 하네스 bridge를 사용했다. 제품 앱의 전체 SwiftUI Scene/탭 라우팅을 검증했다는 뜻은 아니다.
|
||||
|
||||
## 증거 목록
|
||||
|
||||
| 경로 | 내용 |
|
||||
|---|---|
|
||||
| `matrix/` | 현재 소스의 0/1/2/3, 긴 제목, inactive/active, unmount/remount, 축소 시나리오 27 PNG 및 `actions.json` |
|
||||
| `lifecycle/` | phase 전달 자체를 기록한 별도 재현 6 PNG 및 `actions.json` |
|
||||
| `os-*.png`, `os-lifecycle-actions.json` | 실제 Settings 앱으로 background 이동 후 동일 PID로 복귀한 시뮬레이터 화면 3 PNG 및 native lifecycle notification |
|
||||
| `state-callback.json`, `source/ActionCheck.swift` | 실제 상태 helper와 원본 Button 콜백의 별도 드라이버. 10건의 title/전체 HTML 본문/date 동일성 검사와 빈 상태 검사 |
|
||||
| `verification.json`, `ocr.json`, `provenance.json` | 36 PNG의 signature/크기/해시/신선도, Vision OCR 보조 자료, 관측값과 실패 목록, 실제 소스·하네스 출처 |
|
||||
|
||||
`matrix/`와 `lifecycle/`는 실제 window에 대한 `UIGraphicsImageRenderer` + `drawHierarchy(afterScreenUpdates: true)`로 캡처했다. OS 전환 3장은 `simctl io screenshot`이다. 모든 PNG는 최종 하네스 소스 작성 이후 생성됐고 750×1334px다. 합성 화면이나 웹 대체물은 없다. `two-transition-mid.png`는 전환 시점 캡처 시도이며 정확한 중간 프레임이나 애니메이션 성능을 보증하지 않는다.
|
||||
|
||||
before 이미지는 만들지 않았다. 비교 기준은 제공된 기존 title + Spacer + `자세히`/arrow 구조뿐이며, git HEAD 추출·기준 이미지 생성은 하지 않았다.
|
||||
|
||||
## 관측 결과
|
||||
|
||||
| 시나리오 | 판정 | 실제 관측과 증거 |
|
||||
|---|---|---|
|
||||
| 0개 | PASS | `matrix/zero.png`: 배너·화살표 없음 |
|
||||
| 1개 | PASS | `one-initial.png` → `one-after-6s.png`: 시나리오 시작 6.89초 후에도 첫 항목 |
|
||||
| 2개 | PASS | `two-first.png` → `two-second.png` → `two-wrap-first.png`: 첫째 → 둘째 → 첫째. 4.39초 샘플은 첫째, 7.03초는 둘째, 12.21초는 첫째 |
|
||||
| 3개 | PASS | `three-first.png` → `three-second.png` → `three-third.png` → `three-wrap-first.png`: 0.82/6.89/12.05/17.01초에 첫째/둘째/셋째/첫째 |
|
||||
| 긴 한국어 제목·arrow-only | PASS | `long-korean-dark.png`, `long-korean-light.png`: `자세히` 없음, 제목 한 줄 tail truncation, chevron 유지, 겹침 없음. 375pt에서 소스상 제목 예산은 295pt = 375−48−24−8. before 대비 픽셀 증가는 측정하지 않음 |
|
||||
| inactive 중 중지 | **FAIL** | `inactive-start.png`의 둘째가 `inactive-after-6s.png`에서 셋째가 됨. 두 시점 모두 probe는 inactive |
|
||||
| active 복귀 후 재개 | **FAIL** | 실제 active 전달 이후 6.78초가 지나도 셋째 유지. 별도 `lifecycle/phase-active-next.png`에서도 active 주입 후 7.19초 이상 셋째 유지 |
|
||||
| 실제 OS background/복귀 | **FAIL / 하네스 범위** | Settings 앱 이동 시 willResignActive, 같은 PID 복귀 시 didBecomeActive 기록. `os-resume-initial.png`와 `os-resume-after-7s.png`가 같은 첫 항목이며 PNG 해시도 동일. 두 캡처 간 실제 간격은 9.71초 |
|
||||
| unmount/remount | PASS / 관측 범위 | 6초 이상 배너 없음. remount 후 첫째, 5초 전 샘플 첫째, 이후 둘째. 장시간 중복 타이머 부재까지 증명하지 않음 |
|
||||
| 3→1→0 | PASS | `shrink-reset-first.png`에서 첫째로 초기화, 6.88초 후 고정, `shrink-empty.png`에서 숨김 |
|
||||
|
||||
5초 간격 자체는 원본의 `Timer.scheduledTimer(withTimeInterval: 5, repeats: true)`를 그대로 사용했다. 표의 시간은 실제 callback 이후 캡처 완료 uptime이며 시나리오 시작 이후 값이다. 스케줄링 지연이 있으므로 정밀한 5.000초 주기·성능 통과로 해석하지 않는다. 배너 순환을 하네스 알고리즘으로 복제하거나 타이머를 빠르게 바꾸지 않았다.
|
||||
|
||||
### 생명주기 실패 재현
|
||||
|
||||
1. 3개 항목을 표시하고 둘째 항목이 될 때까지 실제 타이머를 기다린다.
|
||||
2. `scenePhase = .inactive`를 주입한다. probe가 inactive를 읽었음을 로그로 확인한다.
|
||||
3. 6초 이상 기다린다. 둘째가 유지돼야 하지만 셋째로 바뀐다.
|
||||
4. `scenePhase = .active`를 주입하고 7초 이상 기다린다. probe는 active지만 셋째가 그대로다.
|
||||
|
||||
이 실패는 matrix와 별도 lifecycle 실행에서 모두 재현했다. 조사 대상은 `MyPageView.swift`의 `.onChange(of: scenePhase)`와 `restartRollTimer()`다. new phase 인자를 무시하고 캡처한 환경 값을 다시 읽는 경로를 우선 확인할 필요가 있으나, 원인 수정이나 대체 제품 구현은 이 QA에서 수행하지 않았다.
|
||||
|
||||
## 상호작용 검증 경계
|
||||
|
||||
**배너 탭·접근성 버튼 활성화는 통과가 아니다.** 하네스의 공개 UIKit 접근성 traversal은 actionable node를 반환하지 않았다. `accessibilityActivateResult=false`, `captureDelta=0`, native GUI sink는 비어 있다. native 화면을 눌렀다거나 실제 detail 화면으로 이동했다고 주장하지 않는다.
|
||||
|
||||
별도 macOS Swift 드라이버는 실제 `MyPageNoticeRoller.current`/`advance()`와 제품 Button에서 그대로 추출한 `AppState.shared.setAppStep(step: .noticeDetail(notice: item))` 콜백을 실행했다. 항목별 title, 전체 HTML content, 원문 date가 sink에 그대로 전달됨을 확인했다. 이것은 상태/콜백 수준 증거이며 화면에 보이는 버튼의 물리적 hit-testing 증거가 아니다.
|
||||
|
||||
미검증: 실제 터치·hit area·detail 화면 렌더링, full MyPage/탭 통합, 로그인·API·실서버·오류 정책, iOS16.6 실제 런타임, 실기기, Dynamic Type·가로모드·다른 폭, VoiceOver 포커스, Reduce Motion 설정 경로, 애니메이션 매끄러움·중단·장시간 타이머 중복. 버튼 label의 소스 높이는 24pt이고 배너 외부 세로 padding은 각각 6pt다. 44pt 터치 영역 확보 여부는 별도 검토가 필요하다.
|
||||
|
||||
## 실행 명령과 정리
|
||||
|
||||
전체 인자·exit code·stdout/stderr·실행 시각은 `commands-matrix.json`, `commands-lifecycle.json`, `commands-os-lifecycle.json`에 기록했다. 아래는 실제 사용한 명령 형태다. `$QA_ROOT`는 작업 종료 시 삭제된 전용 TMP다.
|
||||
|
||||
```bash
|
||||
export QA_ROOT=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk
|
||||
export TMPDIR="$QA_ROOT/tmp/"
|
||||
export NODE_COMPILE_CACHE="$QA_ROOT/node-cache"
|
||||
export BUN_RUNTIME_TRANSPILER_CACHE_PATH="$QA_ROOT/bun-cache"
|
||||
xcrun --sdk iphonesimulator swiftc -sdk "$(xcrun --sdk iphonesimulator --show-sdk-path)" -target x86_64-apple-ios16.6-simulator -module-cache-path "$QA_ROOT/swift-module-cache" "$QA_ROOT/source/"*.swift -o "$QA_ROOT/app/NoticeNativeQA.app/NoticeNativeQA"
|
||||
xcrun simctl install B929E0F0-50F7-46C1-988E-95272D0CE7A2 "$QA_ROOT/app/NoticeNativeQA.app"
|
||||
xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA
|
||||
xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA --lifecycle
|
||||
xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 com.apple.Preferences
|
||||
xcrun simctl io B929E0F0-50F7-46C1-988E-95272D0CE7A2 screenshot "$QA_ROOT/os-resume-after-7s.png"
|
||||
```
|
||||
|
||||
제품 소스 컴파일·설치·실행은 exit 0이었다. 초기 arm64 산출물은 Intel 시뮬레이터에서 설치 거부됐고 x86_64로 해결했다. 하네스 준비 중 read-only `accessibilityReduceMotion` 환경 주입은 컴파일 불가여서 제거했으며 Reduce Motion은 미검증으로 남겼다. 공유 캐시 삭제나 라이브러리 설치는 하지 않았다. 실행 당시 임시 컴파일 디렉터리에는 별도 `ActionCheck.swift`가 없었고, 보존본은 재현을 위해 `source/`에 함께 두었으므로 그 파일을 native 앱 컴파일에 함께 넣으면 안 된다.
|
||||
|
||||
`cleanup.json`이 소유 하네스/Settings 프로세스 종료, 소유 시뮬레이터 shutdown·삭제, TMP root 삭제의 명령과 최종 부재 확인 영수증이다. 전체 앱 빌드·제품 회귀 테스트·독립 리뷰는 부모 작업 범위이며 이 기록은 그 통과를 대신하지 않는다.
|
||||
@@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"command": "xcrun simctl terminate B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167219.060514
|
||||
},
|
||||
{
|
||||
"command": "xcrun --sdk iphonesimulator --show-sdk-path",
|
||||
"exit": 0,
|
||||
"stdout": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167219.0871391
|
||||
},
|
||||
{
|
||||
"command": "xcrun --sdk iphonesimulator swiftc -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk -target x86_64-apple-ios16.6-simulator -module-cache-path /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/swift-module-cache /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/ColorExtension.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/FontModifier.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/GetNoticeResponse.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/Harness.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/MyPageNoticeRoller.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/Spacing.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/UpdateBannerView.swift -o /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/app/NoticeNativeQA.app/NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167227.188478
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl install B929E0F0-50F7-46C1-988E-95272D0CE7A2 /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/app/NoticeNativeQA.app",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167228.378902
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA --lifecycle",
|
||||
"exit": 0,
|
||||
"stdout": "local.sodalive.NoticeNativeQA: 46539\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167229.6560981
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl get_app_container B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA data",
|
||||
"exit": 0,
|
||||
"stdout": "/Users/klaus/Library/Developer/CoreSimulator/Devices/B929E0F0-50F7-46C1-988E-95272D0CE7A2/data/Containers/Data/Application/E6E5317F-E31A-48E8-B5DE-7BBFD7D4C751\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167230.514591
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"command": "xcrun simctl terminate B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167357.147539
|
||||
},
|
||||
{
|
||||
"command": "xcrun --sdk iphonesimulator --show-sdk-path",
|
||||
"exit": 0,
|
||||
"stdout": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167357.190179
|
||||
},
|
||||
{
|
||||
"command": "xcrun --sdk iphonesimulator swiftc -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk -target x86_64-apple-ios16.6-simulator -module-cache-path /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/swift-module-cache /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/ColorExtension.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/FontModifier.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/GetNoticeResponse.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/Harness.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/MyPageNoticeRoller.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/Spacing.swift /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/source/UpdateBannerView.swift -o /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/app/NoticeNativeQA.app/NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167383.799288
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl install B929E0F0-50F7-46C1-988E-95272D0CE7A2 /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/app/NoticeNativeQA.app",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"unix": 1793167387.964253
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "local.sodalive.NoticeNativeQA: 48086\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167392.272796
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl get_app_container B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA data",
|
||||
"exit": 0,
|
||||
"stdout": "/Users/klaus/Library/Developer/CoreSimulator/Devices/B929E0F0-50F7-46C1-988E-95272D0CE7A2/data/Containers/Data/Application/7C9B2E4A-1E90-4868-87FD-4E4E74E9AD5A\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167395.9292119
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,44 @@
|
||||
[
|
||||
{
|
||||
"command": "xcrun simctl get_app_container B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA data",
|
||||
"exit": 0,
|
||||
"stdout": "/Users/klaus/Library/Developer/CoreSimulator/Devices/B929E0F0-50F7-46C1-988E-95272D0CE7A2/data/Containers/Data/Application/E6E5317F-E31A-48E8-B5DE-7BBFD7D4C751\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167320.8572319
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl io B929E0F0-50F7-46C1-988E-95272D0CE7A2 screenshot /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-before-background.png",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "Detected file type 'PNG' from extension\nWrote screenshot to: /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-before-background.png\n",
|
||||
"unix": 1793167321.605435
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 com.apple.Preferences",
|
||||
"exit": 0,
|
||||
"stdout": "com.apple.Preferences: 47181\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167322.518283
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl launch B929E0F0-50F7-46C1-988E-95272D0CE7A2 local.sodalive.NoticeNativeQA",
|
||||
"exit": 0,
|
||||
"stdout": "local.sodalive.NoticeNativeQA: 46539\n",
|
||||
"stderr": "",
|
||||
"unix": 1793167329.793157
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl io B929E0F0-50F7-46C1-988E-95272D0CE7A2 screenshot /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-resume-initial.png",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "Detected file type 'PNG' from extension\nWrote screenshot to: /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-resume-initial.png\n",
|
||||
"unix": 1793167331.456439
|
||||
},
|
||||
{
|
||||
"command": "xcrun simctl io B929E0F0-50F7-46C1-988E-95272D0CE7A2 screenshot /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-resume-after-7s.png",
|
||||
"exit": 0,
|
||||
"stdout": "",
|
||||
"stderr": "Detected file type 'PNG' from extension\nWrote screenshot to: /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/my-notice-native.MtXXGk/os-resume-after-7s.png\n",
|
||||
"unix": 1793167341.17082
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,157 @@
|
||||
[
|
||||
{
|
||||
"event" : "application-didBecomeActive",
|
||||
"scenario" : "preparing",
|
||||
"uptime" : 0.29632126505021006
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 1.1963586449855939
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-first.png",
|
||||
"uptime" : 2.0939663650933653
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-before-inactive",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-before-inactive.png",
|
||||
"uptime" : 8.0479924971004948
|
||||
},
|
||||
{
|
||||
"event" : "inject-inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 9.3028581570833921
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-inactive-start",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-inactive-start.png",
|
||||
"uptime" : 10.481118135037832
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-inactive-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-inactive-after-6s.png",
|
||||
"uptime" : 16.05128803208936
|
||||
},
|
||||
{
|
||||
"event" : "inject-active",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 17.996327791013755
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-active-before-5s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-active-before-5s.png",
|
||||
"uptime" : 21.553165952092968
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-active-next",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-active-next.png",
|
||||
"uptime" : 25.182883065077476
|
||||
},
|
||||
{
|
||||
"event" : "lifecycle-probe-finished",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 27.447052538045682
|
||||
},
|
||||
{
|
||||
"event" : "application-willResignActive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 92.289837046060711
|
||||
},
|
||||
{
|
||||
"event" : "application-didBecomeActive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 100.37458740803413
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,733 @@
|
||||
[
|
||||
{
|
||||
"event" : "application-didBecomeActive",
|
||||
"scenario" : "preparing",
|
||||
"uptime" : 0.50778894103132188
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "zero",
|
||||
"uptime" : 1.2895127300871536
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "zero",
|
||||
"phaseProbe" : [
|
||||
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "zero",
|
||||
"screenshot" : "zero.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 2.1232413940597326
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "one",
|
||||
"uptime" : 3.2916212580166757
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "one-initial",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "one",
|
||||
"screenshot" : "one-initial.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 4.109572621062398
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "one-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "one",
|
||||
"screenshot" : "one-after-6s.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 10.183676649001427
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "two",
|
||||
"uptime" : 11.316744546056725
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "two-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "two",
|
||||
"screenshot" : "two-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 12.137990792980418
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "two-before-5s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "two",
|
||||
"screenshot" : "two-before-5s.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 15.702694575069472
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "two-transition-mid",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "two",
|
||||
"screenshot" : "two-transition-mid.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 16.708362187026069
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "two-second",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "two",
|
||||
"screenshot" : "two-second.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 18.346050271065906
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "two-wrap-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "two",
|
||||
"screenshot" : "two-wrap-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 23.523510750033893
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "three",
|
||||
"uptime" : 24.4716424200451
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "three-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "three",
|
||||
"screenshot" : "three-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 25.293358479975723
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "three-second",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "three",
|
||||
"screenshot" : "three-second.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 31.361499255988747
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "three-third",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "three",
|
||||
"screenshot" : "three-third.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 36.522232864052057
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "three-wrap-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "three",
|
||||
"screenshot" : "three-wrap-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 41.477937716990709
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "long-korean",
|
||||
"uptime" : 43.373455154011026
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "long-korean-dark",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "long-korean",
|
||||
"screenshot" : "long-korean-dark.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 44.191192200989462
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "long-korean-light",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "long-korean",
|
||||
"screenshot" : "long-korean-light.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 46.162646057084203
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "inactive-environment",
|
||||
"uptime" : 47.378562799072824
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "inactive-before",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "inactive-environment",
|
||||
"screenshot" : "inactive-before.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 54.609815668081865
|
||||
},
|
||||
{
|
||||
"event" : "inject-scenePhase-inactive",
|
||||
"scenario" : "inactive-environment",
|
||||
"uptime" : 55.250484918011352
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "inactive-start",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "inactive-environment",
|
||||
"screenshot" : "inactive-start.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 55.97593361698091
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "inactive-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "inactive-environment",
|
||||
"screenshot" : "inactive-after-6s.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 62.475430538062938
|
||||
},
|
||||
{
|
||||
"event" : "inject-scenePhase-active",
|
||||
"scenario" : "inactive-environment",
|
||||
"uptime" : 62.47890109999571
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "reactivated-before-5s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "inactive-environment",
|
||||
"screenshot" : "reactivated-before-5s.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 66.760604207054712
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "reactivated-next",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "inactive-environment",
|
||||
"screenshot" : "reactivated-next.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 69.25886417098809
|
||||
},
|
||||
{
|
||||
"event" : "unmount-banner",
|
||||
"scenario" : "remount",
|
||||
"uptime" : 70.382856977055781
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "unmounted-start",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "remount",
|
||||
"screenshot" : "unmounted-start.png",
|
||||
"uptime" : 71.197345499997027
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "unmounted-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "remount",
|
||||
"screenshot" : "unmounted-after-6s.png",
|
||||
"uptime" : 77.264173908974044
|
||||
},
|
||||
{
|
||||
"event" : "remount-banner",
|
||||
"scenario" : "remount",
|
||||
"uptime" : 77.886691326042637
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "remounted-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "remount",
|
||||
"screenshot" : "remounted-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 78.663667659973726
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "remounted-before-5s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "remount",
|
||||
"screenshot" : "remounted-before-5s.png",
|
||||
"uptime" : 82.855128771974705
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "remounted-second",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "remount",
|
||||
"screenshot" : "remounted-second.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 85.163409232045524
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "shrink-to-one",
|
||||
"uptime" : 87.17370785807725
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "shrink-reset-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "shrink-to-one",
|
||||
"screenshot" : "shrink-reset-first.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 87.98721138201654
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "shrink-one-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "shrink-to-one",
|
||||
"screenshot" : "shrink-one-after-6s.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 94.048975637066178
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "shrink-to-empty",
|
||||
"uptime" : 95.215538030024618
|
||||
},
|
||||
{
|
||||
"accessibilityActivateResult" : false,
|
||||
"captureDelta" : 0,
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "shrink-empty",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "shrink-to-empty",
|
||||
"screenshot" : "shrink-empty.png",
|
||||
"sink" : [
|
||||
|
||||
],
|
||||
"uptime" : 96.02508452697657
|
||||
},
|
||||
{
|
||||
"event" : "matrix-finished",
|
||||
"scenario" : "shrink-to-empty",
|
||||
"uptime" : 97.220463869976811
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,157 @@
|
||||
[
|
||||
{
|
||||
"event" : "application-didBecomeActive",
|
||||
"scenario" : "preparing",
|
||||
"uptime" : 0.29632126505021006
|
||||
},
|
||||
{
|
||||
"event" : "scenario-start",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 1.1963586449855939
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-first",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-first.png",
|
||||
"uptime" : 2.0939663650933653
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-before-inactive",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-before-inactive.png",
|
||||
"uptime" : 8.0479924971004948
|
||||
},
|
||||
{
|
||||
"event" : "inject-inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 9.3028581570833921
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-inactive-start",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-inactive-start.png",
|
||||
"uptime" : 10.481118135037832
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-inactive-after-6s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "inactive",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-inactive-after-6s.png",
|
||||
"uptime" : 16.05128803208936
|
||||
},
|
||||
{
|
||||
"event" : "inject-active",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 17.996327791013755
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-active-before-5s",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-active-before-5s.png",
|
||||
"uptime" : 21.553165952092968
|
||||
},
|
||||
{
|
||||
"elements" : [
|
||||
|
||||
],
|
||||
"event" : "phase-active-next",
|
||||
"phaseProbe" : [
|
||||
{
|
||||
"event" : "appear",
|
||||
"phase" : "active"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "inactive"
|
||||
},
|
||||
{
|
||||
"event" : "change",
|
||||
"phase" : "active"
|
||||
}
|
||||
],
|
||||
"requestedPhase" : "active",
|
||||
"scenario" : "phase-probe",
|
||||
"screenshot" : "phase-active-next.png",
|
||||
"uptime" : 25.182883065077476
|
||||
},
|
||||
{
|
||||
"event" : "lifecycle-probe-finished",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 27.447052538045682
|
||||
},
|
||||
{
|
||||
"event" : "application-willResignActive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 92.289837046060711
|
||||
},
|
||||
{
|
||||
"event" : "application-didBecomeActive",
|
||||
"scenario" : "phase-probe",
|
||||
"uptime" : 100.37458740803413
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"kind": "Native UIKit UIHostingController harness, not full SodaLive app",
|
||||
"captured_at_unix": 1793166510.4207768,
|
||||
"stability_seconds": 15,
|
||||
"files": {
|
||||
"SodaLive/Sources/MyPage/MyPageView.swift": "86a3dee7a4f2ce5bfa99624ff08c0b8eb7c1e88556fc5c04458f7ed2f72c7638",
|
||||
"SodaLive/Sources/V2/MyPage/Models/MyPageNoticeRoller.swift": "1bd3f0dfc2ee59ce692291bf3079b06874e7c1012e4365105d3636928e29fe8a",
|
||||
"SodaLive/Sources/Settings/Notice/GetNoticeResponse.swift": "bbf55eb38659af3df7c6db29af0fa7904d0f87457ec098068fe96e282cee1977",
|
||||
"SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6",
|
||||
"SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364",
|
||||
"SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1",
|
||||
"SodaLive/Resources/Font/Pretendard-Regular.otf": "3ffbacde6ab8411f1d2db54bb9b1f0b3ee2a738932033722cf0388c06aed1c93"
|
||||
},
|
||||
"extracted_update_banner_sha256": "828d5737650436db9a69fd98efc3475362d7045285d2fb8dbdd17870d57047a6",
|
||||
"extraction": "Exact struct UpdateBannerView through next MARK, unchanged; only import SwiftUI prepended",
|
||||
"substitutions": [
|
||||
"AppState and AppStep replaced with local title/content/date capture sink, no routing/server",
|
||||
"LanguageOption reduced to enum cases needed by actual FontModifier; ko locale and app.language=ko forced",
|
||||
"MyPage surrounding page replaced by labeled QA context, fixtures and mount/scenePhase environment driver",
|
||||
"Exact production MyPageNoticeRoller, NoticeItem file, FontModifier, ColorExtension, SodaSpacing compiled",
|
||||
"Real Pretendard-Regular.otf registered in native app Info.plist",
|
||||
"Invisible sibling PhaseProbe records the same injected scenePhase; actual view source unchanged",
|
||||
"UIApplication notification bridge supplies real OS active/inactive to UIKit-hosted SwiftUI environment, not full app Scene"
|
||||
],
|
||||
"baseline": "No before image created; baseline only supplied old title+Spacer+detail/arrow source description",
|
||||
"final_product_hashes_match": true,
|
||||
"verified_after_native_runs_unix": 1793167557.089371,
|
||||
"compiled_sources": {
|
||||
"UpdateBannerView.swift": "466fe05e96ebb70f6ae3fccdf7f62e49526035153c04eaebdeda00a2de125653",
|
||||
"Harness.swift": "e444d9651cb11186a8fcc0792a147e11fe1502f1d044159ece29520d1f456fd8",
|
||||
"Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1",
|
||||
"MyPageNoticeRoller.swift": "1bd3f0dfc2ee59ce692291bf3079b06874e7c1012e4365105d3636928e29fe8a",
|
||||
"ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364",
|
||||
"GetNoticeResponse.swift": "bbf55eb38659af3df7c6db29af0fa7904d0f87457ec098068fe96e282cee1977",
|
||||
"FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6"
|
||||
},
|
||||
"harness_binary_sha256": "3a374bca52920ffe33a9f1e6dac7cfafb526e3abd749e6e0ac32240098de50ad"
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
enum AppStep { case noticeDetail(notice: NoticeItem) }
|
||||
final class AppState {
|
||||
static let shared = AppState()
|
||||
var captured: NoticeItem?
|
||||
func setAppStep(step: AppStep) {
|
||||
switch step { case .noticeDetail(let notice): captured = notice }
|
||||
}
|
||||
}
|
||||
@main struct NativeActionCheck {
|
||||
static func main() throws {
|
||||
let first = NoticeItem(title: "첫 번째 공지 안내", content: "<p>첫 번째 전체 본문</p><p>누락 없이 전달</p>", date: "2026-10-28")
|
||||
let second = NoticeItem(title: "두 번째 서비스 소식", content: "<p>두 번째 전체 본문 & 보존</p>", date: "2026-10-27")
|
||||
let third = NoticeItem(title: "세 번째 업데이트 안내", content: "<p>세 번째 전체 본문</p>\n마지막 줄", date: "2026-10-26")
|
||||
let long = NoticeItem(title: "서비스 업데이트와 새로운 기능을 안내합니다 공지사항의 긴 한국어 제목을 끝까지 확인해 주세요", content: "<p>긴 제목의 전체 본문</p>", date: "2026-10-25")
|
||||
|
||||
var records: [[String: Any]] = []
|
||||
for count in 0...3 {
|
||||
let items = Array([first, second, third].prefix(count))
|
||||
var roller = MyPageNoticeRoller()
|
||||
roller.update(items)
|
||||
for offset in 0...max(count, 1) {
|
||||
guard let item = roller.current else {
|
||||
precondition(count == 0)
|
||||
records.append(["count": count, "current": "none"])
|
||||
break
|
||||
}
|
||||
let callback = { AppState.shared.setAppStep(step: .noticeDetail(notice: item)) }
|
||||
callback()
|
||||
let expected = items[count > 1 ? offset % count : 0]
|
||||
precondition(item == expected && AppState.shared.captured == expected)
|
||||
records.append(["count": count, "offset": offset, "title": item.title, "content": item.content, "date": item.date, "payloadEqual": true])
|
||||
roller.advance()
|
||||
}
|
||||
}
|
||||
var roller = MyPageNoticeRoller()
|
||||
roller.update([long])
|
||||
let item = roller.current!
|
||||
let callback = { AppState.shared.setAppStep(step: .noticeDetail(notice: item)) }
|
||||
callback()
|
||||
precondition(AppState.shared.captured == long)
|
||||
records.append(["count": 1, "title": item.title, "content": item.content, "date": item.date, "payloadEqual": true])
|
||||
let data = try JSONSerialization.data(withJSONObject: ["surface": "actual state helper and extracted Button callback, NOT UI activation", "records": records], options: [.prettyPrinted, .sortedKeys])
|
||||
FileHandle.standardOutput.write(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// ColorExtension.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2023/08/09.
|
||||
//
|
||||
// https://seons-dev.tistory.com/174
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Color {
|
||||
init(hex: String) {
|
||||
let scanner = Scanner(string: hex)
|
||||
_ = scanner.scanString("#")
|
||||
|
||||
var rgb: UInt64 = 0
|
||||
scanner.scanHexInt64(&rgb)
|
||||
|
||||
let r = Double((rgb >> 16) & 0xFF) / 255.0
|
||||
let g = Double((rgb >> 8) & 0xFF) / 255.0
|
||||
let b = Double((rgb >> 0) & 0xFF) / 255.0
|
||||
self.init(red: r, green: g, blue: b)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
convenience init(hex: String, alpha: CGFloat = 1.0) {
|
||||
let scanner = Scanner(string: hex)
|
||||
_ = scanner.scanString("#")
|
||||
|
||||
var rgb: UInt64 = 0
|
||||
scanner.scanHexInt64(&rgb)
|
||||
|
||||
let r = Double((rgb >> 16) & 0xFF) / 255.0
|
||||
let g = Double((rgb >> 8) & 0xFF) / 255.0
|
||||
let b = Double((rgb >> 0) & 0xFF) / 255.0
|
||||
self.init(red: r, green: g, blue: b, alpha: alpha)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// FontModifier.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 1/23/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum SodaTypography {
|
||||
case heading1
|
||||
case heading2
|
||||
case heading3
|
||||
case heading4
|
||||
case body1
|
||||
case body2
|
||||
case body3
|
||||
case body4
|
||||
case body5
|
||||
case body6
|
||||
case caption1
|
||||
case caption2
|
||||
case caption3
|
||||
|
||||
var size: CGFloat {
|
||||
switch self {
|
||||
case .heading1:
|
||||
return 24
|
||||
case .heading2:
|
||||
return 22
|
||||
case .heading3:
|
||||
return 20
|
||||
case .heading4:
|
||||
return 18
|
||||
case .body1, .body2, .body3:
|
||||
return 16
|
||||
case .body4, .body5, .body6:
|
||||
return 14
|
||||
case .caption1, .caption2, .caption3:
|
||||
return 12
|
||||
}
|
||||
}
|
||||
|
||||
func localizedFontName(locale: Locale = .autoupdatingCurrent) -> String? {
|
||||
fontName(for: .localized, weight: weight, locale: locale)
|
||||
}
|
||||
|
||||
var weight: SwiftUI.Font.Weight {
|
||||
switch self {
|
||||
case .heading1, .heading2, .heading3, .heading4, .body1, .body4, .caption1:
|
||||
return .bold
|
||||
case .body2, .body5, .caption2:
|
||||
return .medium
|
||||
case .body3, .body6, .caption3:
|
||||
return .regular
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SodaFontFamily {
|
||||
case localized
|
||||
case pattaya
|
||||
case phosphate
|
||||
}
|
||||
|
||||
private func resolvedLanguageCode(currentLocale: Locale) -> String? {
|
||||
if let raw = UserDefaults.standard.string(forKey: "app.language"),
|
||||
let option = LanguageOption(rawValue: raw),
|
||||
option != .system {
|
||||
return option.rawValue
|
||||
}
|
||||
return currentLocale.language.languageCode?.identifier
|
||||
}
|
||||
|
||||
private func koreanFontName(for weight: SwiftUI.Font.Weight) -> String {
|
||||
switch weight {
|
||||
case .bold:
|
||||
return "Pretendard-Bold"
|
||||
case .medium:
|
||||
return "Pretendard-Medium"
|
||||
case .light:
|
||||
return "Pretendard-Light"
|
||||
default:
|
||||
return "Pretendard-Regular"
|
||||
}
|
||||
}
|
||||
|
||||
private func japaneseFontName(for weight: SwiftUI.Font.Weight) -> String {
|
||||
switch weight {
|
||||
case .bold:
|
||||
return "NotoSansJP-Bold"
|
||||
case .medium:
|
||||
return "NotoSansJP-Medium"
|
||||
case .light:
|
||||
return "NotoSansJP-Light"
|
||||
default:
|
||||
return "NotoSansJP-Regular"
|
||||
}
|
||||
}
|
||||
|
||||
private func fontName(for family: SodaFontFamily, weight: SwiftUI.Font.Weight, locale: Locale) -> String? {
|
||||
switch family {
|
||||
case .localized:
|
||||
switch resolvedLanguageCode(currentLocale: locale) {
|
||||
case "ko":
|
||||
return koreanFontName(for: weight)
|
||||
case "ja":
|
||||
return japaneseFontName(for: weight)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case .pattaya:
|
||||
return "Pattaya-Regular"
|
||||
case .phosphate:
|
||||
return "PhosphateSolid"
|
||||
}
|
||||
}
|
||||
|
||||
struct FontModifier: ViewModifier {
|
||||
@Environment(\.locale) private var locale
|
||||
let size: CGFloat
|
||||
let weight: SwiftUI.Font.Weight
|
||||
let family: SodaFontFamily
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if let fontName = fontName(for: family, weight: weight, locale: locale) {
|
||||
content.font(.custom(fontName, size: size))
|
||||
} else {
|
||||
content.font(.system(size: size, weight: weight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func appFont(
|
||||
size: CGFloat,
|
||||
weight: SwiftUI.Font.Weight = .regular,
|
||||
family: SodaFontFamily = .localized
|
||||
) -> some View {
|
||||
self.modifier(FontModifier(size: size, weight: weight, family: family))
|
||||
}
|
||||
|
||||
func appFont(_ typography: SodaTypography, family: SodaFontFamily = .localized) -> some View {
|
||||
appFont(size: typography.size, weight: typography.weight, family: family)
|
||||
}
|
||||
}
|
||||
|
||||
extension Text {
|
||||
func appFont(
|
||||
size: CGFloat,
|
||||
weight: SwiftUI.Font.Weight = .regular,
|
||||
family: SodaFontFamily = .localized
|
||||
) -> Text {
|
||||
if let fontName = fontName(for: family, weight: weight, locale: .autoupdatingCurrent) {
|
||||
return self.font(.custom(fontName, size: size))
|
||||
}
|
||||
|
||||
return self.font(.system(size: size, weight: weight))
|
||||
}
|
||||
|
||||
func appFont(_ typography: SodaTypography, family: SodaFontFamily = .localized) -> Text {
|
||||
appFont(size: typography.size, weight: typography.weight, family: family)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// GetNoticeResponse.swift
|
||||
// SodaLive
|
||||
//
|
||||
// Created by klaus on 2023/08/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct GetNoticeResponse: Decodable {
|
||||
let totalCount: Int
|
||||
let noticeList: [NoticeItem]
|
||||
}
|
||||
|
||||
struct NoticeItem: Decodable, Hashable {
|
||||
let title: String
|
||||
let content: String
|
||||
let date: String
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
enum LanguageOption: String { case system, ko, en, ja }
|
||||
|
||||
enum AppStep { case noticeDetail(notice: NoticeItem) }
|
||||
|
||||
final class AppState {
|
||||
static let shared = AppState()
|
||||
var captures: [[String: String]] = []
|
||||
var phases: [[String: String]] = []
|
||||
func setAppStep(step: AppStep) {
|
||||
switch step {
|
||||
case .noticeDetail(let notice):
|
||||
captures.append(["title": notice.title, "content": notice.content, "date": notice.date])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PhaseProbe: View {
|
||||
@Environment(\.scenePhase) private var phase
|
||||
var body: some View {
|
||||
Color.clear
|
||||
.onAppear { AppState.shared.phases.append(["event": "appear", "phase": String(describing: phase)]) }
|
||||
.onChange(of: phase) { value in AppState.shared.phases.append(["event": "change", "phase": String(describing: value)]) }
|
||||
}
|
||||
}
|
||||
|
||||
final class HarnessState: ObservableObject {
|
||||
@Published var items: [NoticeItem] = []
|
||||
@Published var mounted = true
|
||||
@Published var phase = ScenePhase.active
|
||||
@Published var scenario = "preparing"
|
||||
}
|
||||
|
||||
struct HarnessRoot: View {
|
||||
@ObservedObject var state: HarnessState
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("NATIVE QA HARNESS").font(.headline)
|
||||
Text(state.scenario).font(.caption.monospaced())
|
||||
Text("Actual UpdateBannerView below").font(.caption)
|
||||
}
|
||||
.foregroundStyle(.white)
|
||||
.padding(24)
|
||||
if state.mounted && !state.items.isEmpty {
|
||||
UpdateBannerView(items: state.items)
|
||||
.background(PhaseProbe())
|
||||
.environment(\.scenePhase, state.phase)
|
||||
}
|
||||
Text("Harness context / no network / no login")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.gray)
|
||||
.padding(24)
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||
.background(Color(hex: "131313"))
|
||||
.environment(\.locale, Locale(identifier: "ko_KR"))
|
||||
}
|
||||
}
|
||||
|
||||
final class HarnessDelegate: NSObject, UIApplicationDelegate {
|
||||
var window: UIWindow?
|
||||
let state = HarnessState()
|
||||
let started = ProcessInfo.processInfo.systemUptime
|
||||
var events: [[String: Any]] = []
|
||||
let output = URL.documentsDirectory.appendingPathComponent(CommandLine.arguments.contains("--lifecycle") ? "lifecycle" : "matrix", isDirectory: true)
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||
UserDefaults.standard.set("ko", forKey: "app.language")
|
||||
try! FileManager.default.createDirectory(at: output, withIntermediateDirectories: true)
|
||||
let window = UIWindow(frame: UIScreen.main.bounds)
|
||||
window.rootViewController = UIHostingController(rootView: HarnessRoot(state: state))
|
||||
window.overrideUserInterfaceStyle = .dark
|
||||
self.window = window
|
||||
window.makeKeyAndVisible()
|
||||
NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: .main) { [weak self] _ in
|
||||
self?.state.phase = .inactive
|
||||
self?.record("application-willResignActive")
|
||||
}
|
||||
NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: .main) { [weak self] _ in
|
||||
self?.state.phase = .active
|
||||
self?.record("application-didBecomeActive")
|
||||
}
|
||||
after(1) { self.run() }
|
||||
return true
|
||||
}
|
||||
|
||||
func after(_ seconds: Double, _ action: @escaping () -> Void) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: action)
|
||||
}
|
||||
|
||||
func record(_ event: String, details: [String: Any] = [:]) {
|
||||
var entry = details
|
||||
entry["event"] = event
|
||||
entry["uptime"] = ProcessInfo.processInfo.systemUptime - started
|
||||
entry["scenario"] = state.scenario
|
||||
events.append(entry)
|
||||
let data = try! JSONSerialization.data(withJSONObject: events, options: [.prettyPrinted, .sortedKeys])
|
||||
try! data.write(to: output.appendingPathComponent("actions.json"))
|
||||
}
|
||||
|
||||
func elements() -> [NSObject] {
|
||||
var found: [NSObject] = []
|
||||
var visited = Set<ObjectIdentifier>()
|
||||
func visit(_ object: NSObject, depth: Int) {
|
||||
guard depth < 30, visited.insert(ObjectIdentifier(object)).inserted else { return }
|
||||
if object.isAccessibilityElement { found.append(object) }
|
||||
if let children = object.accessibilityElements {
|
||||
for child in children { if let child = child as? NSObject { visit(child, depth: depth + 1) } }
|
||||
}
|
||||
let count = object.accessibilityElementCount()
|
||||
if count > 0 && count < 1000 {
|
||||
for index in 0..<count {
|
||||
if let child = object.accessibilityElement(at: index) as? NSObject { visit(child, depth: depth + 1) }
|
||||
}
|
||||
}
|
||||
if let view = object as? UIView { for child in view.subviews { visit(child, depth: depth + 1) } }
|
||||
}
|
||||
if let window { visit(window, depth: 0) }
|
||||
return found
|
||||
}
|
||||
|
||||
func sample(_ name: String, activate: Bool = false, screenshot: Bool = true) {
|
||||
let nodes = elements()
|
||||
var details: [String: Any] = ["elements": nodes.map { node -> [String: Any] in
|
||||
let frame = node.accessibilityFrame
|
||||
return ["class": String(describing: type(of: node)), "label": node.accessibilityLabel ?? "", "button": node.accessibilityTraits.contains(.button), "x": frame.minX, "y": frame.minY, "width": frame.width, "height": frame.height]
|
||||
}]
|
||||
details["phaseProbe"] = AppState.shared.phases
|
||||
details["requestedPhase"] = String(describing: state.phase)
|
||||
if activate {
|
||||
let before = AppState.shared.captures.count
|
||||
let button = nodes.first { node in
|
||||
node.accessibilityTraits.contains(.button) && state.items.contains(where: { node.accessibilityLabel?.contains($0.title) == true })
|
||||
}
|
||||
details["accessibilityActivateResult"] = button?.accessibilityActivate() ?? false
|
||||
details["captureDelta"] = AppState.shared.captures.count - before
|
||||
details["sink"] = AppState.shared.captures
|
||||
}
|
||||
if screenshot, let window {
|
||||
let renderer = UIGraphicsImageRenderer(bounds: window.bounds)
|
||||
let image = renderer.image { _ in window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) }
|
||||
try! image.pngData()!.write(to: output.appendingPathComponent(name + ".png"))
|
||||
details["screenshot"] = name + ".png"
|
||||
}
|
||||
record(name, details: details)
|
||||
}
|
||||
|
||||
func set(_ scenario: String, items: [NoticeItem]) {
|
||||
state.scenario = scenario
|
||||
state.items = items
|
||||
state.phase = .active
|
||||
state.mounted = true
|
||||
record("scenario-start")
|
||||
}
|
||||
|
||||
func run() {
|
||||
let first = NoticeItem(title: "첫 번째 공지 안내", content: "<p>첫 번째 전체 본문</p><p>누락 없이 전달</p>", date: "2026-10-28")
|
||||
let second = NoticeItem(title: "두 번째 서비스 소식", content: "<p>두 번째 전체 본문 & 보존</p>", date: "2026-10-27")
|
||||
let third = NoticeItem(title: "세 번째 업데이트 안내", content: "<p>세 번째 전체 본문</p>\n마지막 줄", date: "2026-10-26")
|
||||
let long = NoticeItem(title: "서비스 업데이트와 새로운 기능을 안내합니다 공지사항의 긴 한국어 제목을 끝까지 확인해 주세요", content: "<p>긴 제목의 전체 본문</p>", date: "2026-10-25")
|
||||
if CommandLine.arguments.contains("--lifecycle") {
|
||||
set("phase-probe", items: [first, second, third])
|
||||
after(0.8) { self.sample("phase-first") }
|
||||
after(6.8) { self.sample("phase-before-inactive") }
|
||||
after(8) { self.state.phase = .inactive; self.record("inject-inactive") }
|
||||
after(8.8) { self.sample("phase-inactive-start") }
|
||||
after(14.8) { self.sample("phase-inactive-after-6s") }
|
||||
after(16) { self.state.phase = .active; self.record("inject-active") }
|
||||
after(20.3) { self.sample("phase-active-before-5s") }
|
||||
after(22.8) { self.sample("phase-active-next") }
|
||||
after(25) { self.record("lifecycle-probe-finished") }
|
||||
return
|
||||
}
|
||||
func stage(_ name: String, _ items: [NoticeItem], samples: [(Double, String)], duration: Double, next: @escaping () -> Void) {
|
||||
self.set(name, items: items)
|
||||
for (delay, identifier) in samples {
|
||||
self.after(delay) { self.sample(identifier, activate: true) }
|
||||
}
|
||||
self.after(duration, next)
|
||||
}
|
||||
stage("zero", [], samples: [(0.7, "zero")], duration: 2) {
|
||||
stage("one", [first], samples: [(0.7, "one-initial"), (6.8, "one-after-6s")], duration: 8) {
|
||||
stage("two", [first, second], samples: [(0.7, "two-first"), (4.3, "two-before-5s"), (5.15, "two-transition-mid"), (6.8, "two-second"), (11.8, "two-wrap-first")], duration: 13) {
|
||||
stage("three", [first, second, third], samples: [(0.7, "three-first"), (6.8, "three-second"), (11.8, "three-third"), (16.8, "three-wrap-first")], duration: 18) {
|
||||
stage("long-korean", [long], samples: [(0.7, "long-korean-dark"), (2.7, "long-korean-light")], duration: 4) {
|
||||
self.window?.overrideUserInterfaceStyle = .dark
|
||||
stage("inactive-environment", [first, second, third], samples: [(6.8, "inactive-before"), (8.2, "inactive-start"), (14.3, "inactive-after-6s"), (19.3, "reactivated-before-5s"), (21.8, "reactivated-next")], duration: 23) {
|
||||
self.state.scenario = "remount"
|
||||
self.state.mounted = false
|
||||
self.record("unmount-banner")
|
||||
self.after(0.7) { self.sample("unmounted-start") }
|
||||
self.after(6.8) { self.sample("unmounted-after-6s") }
|
||||
self.after(7.5) { self.state.mounted = true; self.record("remount-banner") }
|
||||
self.after(8.2) { self.sample("remounted-first", activate: true) }
|
||||
self.after(11.8) { self.sample("remounted-before-5s") }
|
||||
self.after(14.3) { self.sample("remounted-second", activate: true) }
|
||||
self.after(16) {
|
||||
stage("shrink-to-one", [first], samples: [(0.7, "shrink-reset-first"), (6.8, "shrink-one-after-6s")], duration: 8) {
|
||||
stage("shrink-to-empty", [], samples: [(0.7, "shrink-empty")], duration: 2) { self.record("matrix-finished") }
|
||||
}
|
||||
}
|
||||
}
|
||||
self.after(7.5) { self.state.phase = .inactive; self.record("inject-scenePhase-inactive") }
|
||||
self.after(15) { self.state.phase = .active; self.record("inject-scenePhase-active") }
|
||||
}
|
||||
self.after(1.7) { self.window?.overrideUserInterfaceStyle = .light }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct NoticeQAEntry {
|
||||
static func main() { UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(HarnessDelegate.self)) }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0"><dict>
|
||||
<key>CFBundleIdentifier</key><string>local.sodalive.NoticeNativeQA</string>
|
||||
<key>CFBundleExecutable</key><string>NoticeNativeQA</string>
|
||||
<key>CFBundleName</key><string>NoticeNativeQA</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key><true/>
|
||||
<key>MinimumOSVersion</key><string>16.6</string>
|
||||
<key>UIDeviceFamily</key><array><integer>1</integer><integer>2</integer></array>
|
||||
<key>UILaunchScreen</key><dict/>
|
||||
<key>UIAppFonts</key><array><string>Pretendard-Regular.otf</string></array>
|
||||
<key>UISupportedInterfaceOrientations</key><array><string>UIInterfaceOrientationPortrait</string></array>
|
||||
</dict></plist>
|
||||
@@ -0,0 +1,25 @@
|
||||
import Foundation
|
||||
|
||||
struct MyPageNoticeRoller {
|
||||
private var items: [NoticeItem] = []
|
||||
private(set) var index = 0
|
||||
|
||||
var current: NoticeItem? {
|
||||
items.isEmpty ? nil : items[index]
|
||||
}
|
||||
|
||||
mutating func update(_ items: [NoticeItem]) {
|
||||
guard self.items != items else { return }
|
||||
self.items = items
|
||||
index = 0
|
||||
}
|
||||
|
||||
mutating func advance() {
|
||||
guard items.count > 1 else { return }
|
||||
index = (index + 1) % items.count
|
||||
}
|
||||
|
||||
func shouldRoll(isVisible: Bool, isActive: Bool) -> Bool {
|
||||
items.count > 1 && isVisible && isActive
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Spacing.swift
|
||||
// SodaLive
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum SodaSpacing {
|
||||
static let s4: CGFloat = 4
|
||||
static let s6: CGFloat = 6
|
||||
static let s8: CGFloat = 8
|
||||
static let s12: CGFloat = 12
|
||||
static let s14: CGFloat = 14
|
||||
static let s16: CGFloat = 16
|
||||
static let s20: CGFloat = 20
|
||||
static let s24: CGFloat = 24
|
||||
static let s28: CGFloat = 28
|
||||
static let s32: CGFloat = 32
|
||||
static let s48: CGFloat = 48
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import SwiftUI
|
||||
|
||||
struct UpdateBannerView: View {
|
||||
private static let foregroundColor = Color(hex: "B0BEC5")
|
||||
private static let rollInterval: TimeInterval = 5
|
||||
private static let transitionDuration: TimeInterval = 0.35
|
||||
|
||||
let items: [NoticeItem]
|
||||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
@Environment(\.accessibilityReduceMotion) private var reduceMotion
|
||||
@State private var roller = MyPageNoticeRoller()
|
||||
@State private var rollTimer: Timer?
|
||||
@State private var isVisible = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let item = roller.current {
|
||||
Button {
|
||||
AppState.shared.setAppStep(step: .noticeDetail(notice: item))
|
||||
} label: {
|
||||
HStack(spacing: SodaSpacing.s8) {
|
||||
Text(item.title)
|
||||
.appFont(.body3)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.frame(width: SodaSpacing.s24, height: SodaSpacing.s24)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.foregroundColor(Self.foregroundColor)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.id(roller.index)
|
||||
.transition(reduceMotion ? .identity : .asymmetric(
|
||||
insertion: .move(edge: .bottom),
|
||||
removal: .move(edge: .top)
|
||||
))
|
||||
}
|
||||
}
|
||||
.clipped()
|
||||
.padding(.horizontal, SodaSpacing.s24)
|
||||
.padding(.vertical, SodaSpacing.s6)
|
||||
.background(Color.black)
|
||||
.onAppear {
|
||||
isVisible = true
|
||||
roller.update(items)
|
||||
restartRollTimer()
|
||||
}
|
||||
.onDisappear {
|
||||
isVisible = false
|
||||
stopRollTimer()
|
||||
}
|
||||
.onChange(of: items) { items in
|
||||
roller.update(items)
|
||||
restartRollTimer()
|
||||
}
|
||||
.onChange(of: scenePhase) { _ in
|
||||
restartRollTimer()
|
||||
}
|
||||
.onChange(of: reduceMotion) { _ in
|
||||
restartRollTimer()
|
||||
}
|
||||
}
|
||||
|
||||
private func restartRollTimer() {
|
||||
stopRollTimer()
|
||||
guard roller.shouldRoll(isVisible: isVisible, isActive: scenePhase == .active) else { return }
|
||||
|
||||
rollTimer = Timer.scheduledTimer(withTimeInterval: Self.rollInterval, repeats: true) { _ in
|
||||
withAnimation(reduceMotion ? nil : .easeOut(duration: Self.transitionDuration)) {
|
||||
roller.advance()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func stopRollTimer() {
|
||||
rollTimer?.invalidate()
|
||||
rollTimer = nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"records" : [
|
||||
{
|
||||
"count" : 0,
|
||||
"current" : "none"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 1,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 0,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 1,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 1,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 2,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 0,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>두 번째 전체 본문 & 보존<\/p>",
|
||||
"count" : 2,
|
||||
"date" : "2026-10-27",
|
||||
"offset" : 1,
|
||||
"payloadEqual" : true,
|
||||
"title" : "두 번째 서비스 소식"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 2,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 2,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 3,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 0,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>두 번째 전체 본문 & 보존<\/p>",
|
||||
"count" : 3,
|
||||
"date" : "2026-10-27",
|
||||
"offset" : 1,
|
||||
"payloadEqual" : true,
|
||||
"title" : "두 번째 서비스 소식"
|
||||
},
|
||||
{
|
||||
"content" : "<p>세 번째 전체 본문<\/p>\n마지막 줄",
|
||||
"count" : 3,
|
||||
"date" : "2026-10-26",
|
||||
"offset" : 2,
|
||||
"payloadEqual" : true,
|
||||
"title" : "세 번째 업데이트 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>첫 번째 전체 본문<\/p><p>누락 없이 전달<\/p>",
|
||||
"count" : 3,
|
||||
"date" : "2026-10-28",
|
||||
"offset" : 3,
|
||||
"payloadEqual" : true,
|
||||
"title" : "첫 번째 공지 안내"
|
||||
},
|
||||
{
|
||||
"content" : "<p>긴 제목의 전체 본문<\/p>",
|
||||
"count" : 1,
|
||||
"date" : "2026-10-25",
|
||||
"payloadEqual" : true,
|
||||
"title" : "서비스 업데이트와 새로운 기능을 안내합니다 공지사항의 긴 한국어 제목을 끝까지 확인해 주세요"
|
||||
}
|
||||
],
|
||||
"surface" : "actual state helper and extracted Button callback, NOT UI activation"
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
{
|
||||
"verdict": "NEEDS WORK: lifecycle fails; not a full app approval",
|
||||
"imageCount": 36,
|
||||
"images": [
|
||||
{
|
||||
"file": "lifecycle/phase-active-before-5s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a69fa393ba3c4cb35d59755608d8ba76bf9c0e8a6c47cb583a7ab4ffe2836bfe",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "lifecycle/phase-active-next.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a69fa393ba3c4cb35d59755608d8ba76bf9c0e8a6c47cb583a7ab4ffe2836bfe",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "lifecycle/phase-before-inactive.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "103a498d121e35cd7c0890cfdd0e9093b8dc99d822642dbd3ea8decf6830e9c7",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "lifecycle/phase-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "21584cbfbf9e646b76a04e8ea1a48bbffef57f018a8bf6b923056da1a3075d3d",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "lifecycle/phase-inactive-after-6s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a69fa393ba3c4cb35d59755608d8ba76bf9c0e8a6c47cb583a7ab4ffe2836bfe",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "lifecycle/phase-inactive-start.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "103a498d121e35cd7c0890cfdd0e9093b8dc99d822642dbd3ea8decf6830e9c7",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/inactive-after-6s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "cd73444cafc0cce267b60e0c3d3cd5d72a1520c62e5297e6e5b56b560925d6b0",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/inactive-before.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "e079945c4c6991682cb9cd0d10341cb616242466e0508c6f00a0e61338bc481c",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/inactive-start.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "e079945c4c6991682cb9cd0d10341cb616242466e0508c6f00a0e61338bc481c",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/long-korean-dark.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "811c747563f3c3eef3811054b17f96e3821bfa03a08413ab16b4fc92fd861f10",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/long-korean-light.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "811c747563f3c3eef3811054b17f96e3821bfa03a08413ab16b4fc92fd861f10",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/one-after-6s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "d5bd330038376241762a2cd6cb723f072f32b0f125de774bab72f3d3fc426433",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/one-initial.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "d5bd330038376241762a2cd6cb723f072f32b0f125de774bab72f3d3fc426433",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/reactivated-before-5s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "cd73444cafc0cce267b60e0c3d3cd5d72a1520c62e5297e6e5b56b560925d6b0",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/reactivated-next.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "cd73444cafc0cce267b60e0c3d3cd5d72a1520c62e5297e6e5b56b560925d6b0",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/remounted-before-5s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "0d820088fe35e747f35e59fdd8854d4b1a85dea83d8d055770f9b97ace627cd9",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/remounted-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "0d820088fe35e747f35e59fdd8854d4b1a85dea83d8d055770f9b97ace627cd9",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/remounted-second.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "b782aa7eba591e190e60b8bc2b58c353fa358415f410384049eb2052e964c375",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/shrink-empty.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "89f18c85fe95fa5a9a126cfee45e1185b7d21018943dae12e65939008ffa8fae",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/shrink-one-after-6s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "14a8d4fe2b9cb8df4d843c6337830efa1b8c304ea7a83313a48dd5945e434dbe",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/shrink-reset-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "14a8d4fe2b9cb8df4d843c6337830efa1b8c304ea7a83313a48dd5945e434dbe",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/three-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "6290705ceaea19c4bc7702ddcda83e30ab68ef1da46072121ab627e73f89ebb9",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/three-second.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "5ad43d2dc2e39a53e31a08792b0d0280eb36a3ce46b0f29152a49db107f6b23f",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/three-third.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "96494648b7e1653e83b620f3bf69c664921bd0bc3cf35da451430be2fd5469e4",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/three-wrap-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "6290705ceaea19c4bc7702ddcda83e30ab68ef1da46072121ab627e73f89ebb9",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/two-before-5s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a92e3ab9e7d8892eb19187f656f881861ee4ca0443a44c6282f0716a403a6186",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/two-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a92e3ab9e7d8892eb19187f656f881861ee4ca0443a44c6282f0716a403a6186",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/two-second.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "028ac53f80d69ee0e693e33043133f4339b4cba38465e7d192ac1877689f2796",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/two-transition-mid.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "7864b94177cd6daffa376044ff1a73d619a8421c8a63be589d0da042bd1b609d",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/two-wrap-first.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "a92e3ab9e7d8892eb19187f656f881861ee4ca0443a44c6282f0716a403a6186",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/unmounted-after-6s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "55600884241b926a4ff02b712cf7ea5162b5e937af192bbc7a177c7c4c4f5a43",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/unmounted-start.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "55600884241b926a4ff02b712cf7ea5162b5e937af192bbc7a177c7c4c4f5a43",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "matrix/zero.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "c8fabbad2e028ee0c2a89bc4841bf5b2233e2eb3ee33f1d25ae2d4e09f6069ad",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "os-before-background.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "3a16c67bf881dc7405c9f04aebf90d08720567d9408420e47f9e511080da6b24",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "os-resume-after-7s.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "65c773fef1546972fc644c96fa1e8a1b11c2635cbbda55723f2dfb0df5059487",
|
||||
"fresh": true
|
||||
},
|
||||
{
|
||||
"file": "os-resume-initial.png",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"sha256": "65c773fef1546972fc644c96fa1e8a1b11c2635cbbda55723f2dfb0df5059487",
|
||||
"fresh": true
|
||||
}
|
||||
],
|
||||
"checks": [
|
||||
{
|
||||
"screenshot": "matrix/zero.png",
|
||||
"expected": "",
|
||||
"actualOCR": "",
|
||||
"pass": true,
|
||||
"uptime": 2.1232413940597326,
|
||||
"secondsAfterScenarioStart": 0.8337286639725789
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/one-initial.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 4.109572621062398,
|
||||
"secondsAfterScenarioStart": 0.8179513630457222
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/one-after-6s.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 10.183676649001427,
|
||||
"secondsAfterScenarioStart": 6.892055390984751
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/two-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 12.137990792980418,
|
||||
"secondsAfterScenarioStart": 0.8212462469236925
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/two-before-5s.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 15.702694575069472,
|
||||
"secondsAfterScenarioStart": 4.385950029012747
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/two-second.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "두 번째 서비스 소식",
|
||||
"pass": true,
|
||||
"uptime": 18.346050271065906,
|
||||
"secondsAfterScenarioStart": 7.029305725009181
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/two-wrap-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 23.523510750033893,
|
||||
"secondsAfterScenarioStart": 12.206766203977168
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/three-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 25.293358479975723,
|
||||
"secondsAfterScenarioStart": 0.8217160599306226
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/three-second.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "두 번째 서비스 소식",
|
||||
"pass": true,
|
||||
"uptime": 31.361499255988747,
|
||||
"secondsAfterScenarioStart": 6.889856835943647
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/three-third.png",
|
||||
"expected": "세 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": true,
|
||||
"uptime": 36.52223286405206,
|
||||
"secondsAfterScenarioStart": 12.050590444006957
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/three-wrap-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 41.47793771699071,
|
||||
"secondsAfterScenarioStart": 17.00629529694561
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/long-korean-dark.png",
|
||||
"expected": "서비스 업데이트",
|
||||
"actualOCR": "서비스 업데이트와 새로운 기능을 안내합니다...",
|
||||
"pass": true,
|
||||
"uptime": 44.19119220098946,
|
||||
"secondsAfterScenarioStart": 0.8177370469784364
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/long-korean-light.png",
|
||||
"expected": "서비스 업데이트",
|
||||
"actualOCR": "서비스 업데이트와 새로운 기능을 안내합니다...",
|
||||
"pass": true,
|
||||
"uptime": 46.1626460570842,
|
||||
"secondsAfterScenarioStart": 2.7891909030731767
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/inactive-before.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "두 번째 서비스 소식",
|
||||
"pass": true,
|
||||
"uptime": 54.609815668081865,
|
||||
"secondsAfterScenarioStart": 7.23125286900904
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/inactive-start.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "두 번째 서비스 소식",
|
||||
"pass": true,
|
||||
"uptime": 55.97593361698091,
|
||||
"secondsAfterScenarioStart": 8.597370817908086
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/inactive-after-6s.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": false,
|
||||
"uptime": 62.47543053806294,
|
||||
"secondsAfterScenarioStart": 15.096867738990113
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/reactivated-before-5s.png",
|
||||
"expected": "세 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": true,
|
||||
"uptime": 66.76060420705471,
|
||||
"secondsAfterScenarioStart": 19.382041407981887
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/reactivated-next.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": false,
|
||||
"uptime": 69.25886417098809,
|
||||
"secondsAfterScenarioStart": 21.880301371915266
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/unmounted-start.png",
|
||||
"expected": "",
|
||||
"actualOCR": "",
|
||||
"pass": true,
|
||||
"uptime": 71.19734549999703,
|
||||
"secondsAfterScenarioStart": 0.8144885229412466
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/unmounted-after-6s.png",
|
||||
"expected": "",
|
||||
"actualOCR": "",
|
||||
"pass": true,
|
||||
"uptime": 77.26417390897404,
|
||||
"secondsAfterScenarioStart": 6.881316931918263
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/remounted-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 78.66366765997373,
|
||||
"secondsAfterScenarioStart": 8.280810682917945
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/remounted-before-5s.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 82.8551287719747,
|
||||
"secondsAfterScenarioStart": 12.472271794918925
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/remounted-second.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "두 번째 서비스 소식",
|
||||
"pass": true,
|
||||
"uptime": 85.16340923204552,
|
||||
"secondsAfterScenarioStart": 14.780552254989743
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/shrink-reset-first.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 87.98721138201654,
|
||||
"secondsAfterScenarioStart": 0.8135035239392892
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/shrink-one-after-6s.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "첫 번째 공지 안내",
|
||||
"pass": true,
|
||||
"uptime": 94.04897563706618,
|
||||
"secondsAfterScenarioStart": 6.875267778988928
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/shrink-empty.png",
|
||||
"expected": "",
|
||||
"actualOCR": "",
|
||||
"pass": true,
|
||||
"uptime": 96.02508452697657,
|
||||
"secondsAfterScenarioStart": 0.8095464969519526
|
||||
},
|
||||
{
|
||||
"before": "lifecycle/phase-inactive-start.png",
|
||||
"after": "lifecycle/phase-inactive-after-6s.png",
|
||||
"expectedSameTitle": true,
|
||||
"actualSameTitle": false,
|
||||
"beforeOCR": "두 번째 서비스 소식",
|
||||
"afterOCR": "세 번째 업데이트 안내",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"before": "lifecycle/phase-active-before-5s.png",
|
||||
"after": "lifecycle/phase-active-next.png",
|
||||
"expectedSameTitle": false,
|
||||
"actualSameTitle": true,
|
||||
"beforeOCR": "세 번째 업데이트 안내",
|
||||
"afterOCR": "세 번째 업데이트 안내",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"before": "os-resume-initial.png",
|
||||
"after": "os-resume-after-7s.png",
|
||||
"expectedSameTitle": false,
|
||||
"actualSameTitle": true,
|
||||
"beforeOCR": "첫 번째 공지 안내",
|
||||
"afterOCR": "첫 번째 공지 안내",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"failedChecks": [
|
||||
{
|
||||
"screenshot": "matrix/inactive-after-6s.png",
|
||||
"expected": "두 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": false,
|
||||
"uptime": 62.47543053806294,
|
||||
"secondsAfterScenarioStart": 15.096867738990113
|
||||
},
|
||||
{
|
||||
"screenshot": "matrix/reactivated-next.png",
|
||||
"expected": "첫 번째",
|
||||
"actualOCR": "세 번째 업데이트 안내",
|
||||
"pass": false,
|
||||
"uptime": 69.25886417098809,
|
||||
"secondsAfterScenarioStart": 21.880301371915266
|
||||
},
|
||||
{
|
||||
"before": "lifecycle/phase-inactive-start.png",
|
||||
"after": "lifecycle/phase-inactive-after-6s.png",
|
||||
"expectedSameTitle": true,
|
||||
"actualSameTitle": false,
|
||||
"beforeOCR": "두 번째 서비스 소식",
|
||||
"afterOCR": "세 번째 업데이트 안내",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"before": "lifecycle/phase-active-before-5s.png",
|
||||
"after": "lifecycle/phase-active-next.png",
|
||||
"expectedSameTitle": false,
|
||||
"actualSameTitle": true,
|
||||
"beforeOCR": "세 번째 업데이트 안내",
|
||||
"afterOCR": "세 번째 업데이트 안내",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"before": "os-resume-initial.png",
|
||||
"after": "os-resume-after-7s.png",
|
||||
"expectedSameTitle": false,
|
||||
"actualSameTitle": true,
|
||||
"beforeOCR": "첫 번째 공지 안내",
|
||||
"afterOCR": "첫 번째 공지 안내",
|
||||
"pass": false
|
||||
}
|
||||
],
|
||||
"interactionLimit": "Native accessibility traversal returned no actionable nodes. accessibilityActivate false, sinkDelta=0. Physical tap and UI navigation untested. Separate real helper/extracted callback driver payload checks passed.",
|
||||
"sourceHashStable": true
|
||||
}
|
||||