also looking at this
fix(native): preserve workspace sources and native overlay lifecycle
#384
20 files
+500
-227
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -623,6 +623,9 @@ jobs: | ||
| 623 | 623 | artifacts/mobile-e2e/ios-zig-slice/console.plain |
| 624 | 624 | artifacts/mobile-e2e/ios-zig-slice/app-stdout.log |
| 625 | 625 | artifacts/mobile-e2e/ios-zig-slice/app-stderr.log |
| 626 | artifacts/mobile-e2e/ios-zig-slice/failure.png | |
| 627 | artifacts/mobile-e2e/ios-zig-slice/failure-screenshot.log | |
| 628 | artifacts/mobile-e2e/ios-zig-slice/failure-location.log | |
| 626 | 629 | if-no-files-found: error |
| 627 | 630 | retention-days: 30 |
| 628 | 631 | |
| @@ -2614,7 +2614,7 @@ final class NativeRenderUnitTests: XCTestCase { | ||
| 2614 | 2614 | let controller = CraftNativeScreenController(config: CraftConfig()) |
| 2615 | 2615 | controller.loadViewIfNeeded() |
| 2616 | 2616 | let screen: ([String: Any]) -> [String: Any] = { props in |
| 2617 | document([[ | |
| 2617 | self.document([[ | |
| 2618 | 2618 | "type": "Switch", |
| 2619 | 2619 | "props": ["key": "consent", "value": props["value"] as Any], |
| 2620 | 2620 | "events": ["onValueChange": "consentChanged"], |
| @@ -137,6 +137,26 @@ SIMCTL_CHILD_NSUnbufferedIO=YES xcrun simctl launch --terminate-running-process | ||
| 137 | 137 | |
| 138 | 138 | gather_log() { cat "$STDOUT_LOG" "$STDERR_LOG" > "$LOG" 2>/dev/null || true; } |
| 139 | 139 | |
| 140 | collect_failure_diagnostics() { | |
| 141 | # Read-only evidence from this simulator, bounded independently of the | |
| 142 | # unchanged assertion deadline. Never dismiss alerts or change permissions. | |
| 143 | python3 - "$UDID" "$OUT" <<'PY' | |
| 144 | import pathlib, subprocess, sys | |
| 145 | device, output = sys.argv[1], pathlib.Path(sys.argv[2]) | |
| 146 | commands = [ | |
| 147 | ("failure-screenshot.log", ["xcrun", "simctl", "io", device, "screenshot", str(output / "failure.png")]), | |
| 148 | ("failure-location.log", ["xcrun", "simctl", "spawn", device, "log", "show", "--style", "compact", "--last", "20m", "--predicate", 'process == "locationd" OR process == "tccd"']), | |
| 149 | ] | |
| 150 | for name, command in commands: | |
| 151 | with (output / name).open("w") as evidence: | |
| 152 | try: | |
| 153 | result = subprocess.run(command, stdout=evidence, stderr=subprocess.STDOUT, timeout=10) | |
| 154 | evidence.write(f"\nDiagnostic exit: {result.returncode}\n") | |
| 155 | except (OSError, subprocess.TimeoutExpired) as error: | |
| 156 | evidence.write(f"\nDiagnostic unavailable: {error}\n") | |
| 157 | PY | |
| 158 | } | |
| 159 | ||
| 140 | 160 | # The round trip is fast, but a cold simulator is not. Poll rather than sleep a |
| 141 | 161 | # fixed amount, so a slow boot does not read as a failure. |
| 142 | 162 | # |
| @@ -179,7 +199,7 @@ while :; do | ||
| 179 | 199 | cat "$LOG" || true |
| 180 | 200 | echo "FAIL: timed out after ${SLICE_TIMEOUT}s waiting for:$MISSING" |
| 181 | 201 | echo " The app never got that far — this is a timeout, not a wrong answer." |
| 182 | echo " Raise CRAFT_SLICE_TIMEOUT if the machine is simply slow." | |
| 202 | collect_failure_diagnostics | |
| 183 | 203 | xcrun simctl terminate "$UDID" "$BUNDLE_ID" 2>/dev/null || true |
| 184 | 204 | exit 1 |
| 185 | 205 | fi |
| @@ -1882,8 +1882,15 @@ describe('native renderer component contract', () => { | ||
| 1882 | 1882 | expect(guide).toContain('each ancestor `onScroll` event as placement invalidations') |
| 1883 | 1883 | expect(iosReadme).toContain('host-neutral invalidation signals for') |
| 1884 | 1884 | expect(androidReadme).toContain('host-neutral invalidation signals for') |
| 1885 | expect(androidReadme).toContain('do not claim Health Services') | |
| 1886 | expect(guide).toContain('provide Health Services background execution or live sensor samples') | |
| 1885 | }) | |
| 1886 | ||
| 1887 | it('documents optional native Health Services with capability checks and explicit failures', () => { | |
| 1888 | expect(androidReadme).toContain('`enableHealthServices: true` to retain `ExerciseClient` in a foreground') | |
| 1889 | expect(androidReadme).toContain('"enableHealthServices": false') | |
| 1890 | expect(androidReadme).toContain('emit a typed `error` event and leave the Health Connect fallback untouched') | |
| 1891 | expect(guide).toContain('`enableHealthServices: true`. Craft retains `ExerciseClient` in a foreground') | |
| 1892 | expect(guide).toContain('checks device capabilities before starting') | |
| 1893 | expect(guide).toContain('produce an `error` event rather than silently upgrading the fallback') | |
| 1887 | 1894 | }) |
| 1888 | 1895 | |
| 1889 | 1896 | it('exposes screen-owned scrolling for mounted native scroll views', () => { |