ReviewOS

also looking at this

craft-native/craft

feat(maps): add key-free native Android maps and shared map controls

#363
Merged chris wants to merge codex/wildloop-native-map-providers into main
28 files +1857 -481

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.

packages/ios/src/testing.test.tsmodified+18-0
Changes to packages/ios/src/testing.test.ts
@@ -76,6 +76,24 @@ describe('package-owned iOS shells', () => {
7676 }
7777 })
7878
79 it('serializes native slider interactions and verifies app handoffs without launching another app', () => {
80 expect(serializeSimulatorSteps([
81 { command: 'sliderid', id: 'elevation', position: 0.5 },
82 { command: 'expectapp', text: 'com.apple.Maps', seconds: 10 },
83 ])).toBe('sliderid elevation 0.5\nexpectapp com.apple.Maps | 10\n')
84 for (const position of [-1, 1.1, NaN, Infinity])
85 expect(() => serializeSimulatorSteps([{ command: 'sliderid', id: 'elevation', position }])).toThrow()
86 expect(() => serializeSimulatorSteps([{ command: 'sliderid', id: 'two sliders', position: 0.5 }])).toThrow()
87 expect(() => serializeSimulatorSteps([{ command: 'expectapp', text: 'Maps\nlaunch' }])).toThrow()
88 const output = folder()
89 initSimulatorDriver({ output, bundleId: 'org.example.app' })
90 const source = readFileSync(join(output, 'UITests/DriverTests.swift'), 'utf8')
91 expect(source).toContain('slider.adjust(toNormalizedSliderPosition: CGFloat(position))')
92 const handoff = source.slice(source.indexOf('case "expectapp":'), source.indexOf('case "field":'))
93 expect(handoff).toContain('target.wait(for: .runningForeground')
94 expect(handoff).not.toContain('target.launch()')
95 })
96
7997 it('bundles an on-device journey with native XCTest evidence and removes it when returning to server mode', () => {
8098 const output = folder()
8199 const options = { output, bundleId: 'org.example.app', script: 'activate\nshot current\ntree current\n' }
packages/ios/src/testing.tsmodified+10-0
Changes to packages/ios/src/testing.ts
@@ -91,6 +91,10 @@ export type SimulatorStep =
9191 | { command: 'touches', paths: TimedTouchPoint[][] }
9292 | { command: 'field', index: number, text: string }
9393 | { command: 'typeid' | 'labelid', id: string, text: string }
94 /** Adjust an actual native UISlider by identifier, using its normalized range. */
95 | { command: 'sliderid', id: string, position: number }
96 /** Assert a handoff reached the foreground application without launching it. */
97 | { command: 'expectapp', text: string, seconds?: number }
9498 | { command: 'expect' | 'expectexact' | 'expectid' | 'systemexpect', text: string, seconds?: number }
9599 | { command: 'tap' | 'tapif' | 'tapid' | 'tapexact' | 'type' | 'absent' | 'absentid' | 'selected' | 'tab' | 'scrollto' | 'dismiss' | 'log' | 'systemtap' | 'systemabsent', text: string }
96100 | { command: 'shot' | 'tree' | 'audit' | 'exec' | 'systemtree', name: string }
@@ -145,9 +149,15 @@ export function serializeSimulatorSteps(steps: readonly SimulatorStep[]): string
145149 if (!Number.isInteger(number(step.index))) throw new Error('field index must be an integer')
146150 arg = `${step.index} ${step.text}`
147151 }
152 else if ('position' in step) {
153 if (number(step.position) > 1) throw new Error('Slider position must be between 0 and 1')
154 arg = `${token(step.id)} ${step.position}`
155 }
148156 else if ('id' in step) arg = `${token(step.id)} ${step.text}`
149157 else if ('text' in step) {
150158 arg = step.text
159 if (step.command === 'expectapp' && !/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(arg))
160 throw new Error('Expected an application bundle identifier')
151161 if ('seconds' in step && step.seconds !== undefined) {
152162 if (arg.includes('|')) throw new Error('A timed expectation cannot contain |')
153163 arg += ` | ${number(step.seconds)}`
packages/ios/templates/testing/DriverTests.swift.templatemodified+19-0
Changes to packages/ios/templates/testing/DriverTests.swift.template
@@ -29,6 +29,8 @@ import Darwin
2929/// return the keyboard's return key (Go on a sign-in form)
3030/// field <n> <text> tap the n-th text field (0-based, secure fields after plain) and type
3131/// typeid <identifier> <text> tap the element with this identifier and type
32/// sliderid <identifier> <0…1> adjusts a hittable native slider
33/// expectapp <bundle-id> [| secs] asserts a native app handoff reached the foreground
3234/// expect <text> [| secs] waits for an element containing text; logs PASS/FAIL
3335/// expectexact <label> [| secs] a button or text whose label is exactly this
3436/// absent <text> logs PASS when no element contains text
@@ -360,6 +362,23 @@ final class DriverTests: XCTestCase {
360362 app.typeText(idParts.count > 1 ? idParts[1] : "")
361363 note("typeid \(idParts[0]): ok")
362364 } else { XCTFail("Element not found"); note("FAIL typeid \(idParts[0]): NOT FOUND") }
365 case "sliderid":
366 let parts = arg.split(separator: " ", maxSplits: 1).map(String.init)
367 guard parts.count == 2, let position = Double(parts[1]), position.isFinite, (0...1).contains(position) else {
368 check(false, "sliderid requires an identifier and normalized position")
369 return
370 }
371 let slider = app.sliders.matching(NSPredicate(format: "identifier == %@", parts[0])).firstMatch
372 guard slider.waitForExistence(timeout: 10), slider.isHittable else {
373 check(false, "sliderid \(parts[0]) is hittable")
374 return
375 }
376 slider.adjust(toNormalizedSliderPosition: CGFloat(position))
377 note("sliderid \(parts[0]): \(position)")
378 case "expectapp":
379 let (bundle, seconds) = split(arg)
380 let target = XCUIApplication(bundleIdentifier: bundle)
381 check(target.wait(for: .runningForeground, timeout: seconds), "expectapp \(bundle)")
363382 case "field":
364383 let fieldParts = arg.split(separator: " ", maxSplits: 1).map(String.init)
365384 let index = Int(fieldParts[0]) ?? 0