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+16-1
Changes to packages/ios/src/testing.test.ts
@@ -94,6 +94,21 @@ describe('package-owned iOS shells', () => {
9494 expect(handoff).not.toContain('target.launch()')
9595 })
9696
97 it('checks actual native field values after a real edit-menu gesture', () => {
98 expect(serializeSimulatorSteps([
99 { command: 'longpressid', text: 'route-name' },
100 { command: 'tapexact', text: 'Paste' },
101 { command: 'valueid', id: 'route-name', text: 'https://example.org/trail/7' },
102 ])).toBe('longpressid route-name\ntapexact Paste\nvalueid route-name https://example.org/trail/7\n')
103 expect(() => serializeSimulatorSteps([{ command: 'longpressid', text: 'two fields' }])).toThrow()
104 expect(() => serializeSimulatorSteps([{ command: 'valueid', id: 'route-name', text: 'x\nlaunch' }])).toThrow()
105 const output = folder()
106 initSimulatorDriver({ output, bundleId: 'org.example.app' })
107 const source = readFileSync(join(output, 'UITests/DriverTests.swift'), 'utf8')
108 expect(source).toContain('target.press(forDuration: 1.2)')
109 expect(source).toContain('(target.value as? String) == want')
110 })
111
97112 it('bundles an on-device journey with native XCTest evidence and removes it when returning to server mode', () => {
98113 const output = folder()
99114 const options = { output, bundleId: 'org.example.app', script: 'activate\nshot current\ntree current\n' }
@@ -155,7 +170,7 @@ describe('package-owned iOS shells', () => {
155170 initSimulatorDriver({ output, bundleId: 'org.example.app' })
156171 const source = readFileSync(join(output, 'UITests/DriverTests.swift'), 'utf8')
157172 const handled = new Set([...source.matchAll(/case "([a-z]+)"/g)].map(match => match[1]))
158 const commands: SimulatorStep['command'][] = ['launch', 'activate', 'terminate', 'home', 'back', 'return', 'unlabelled', 'background', 'wait', 'delay', 'net', 'swipe', 'drag', 'edge', 'tabbar', 'tapxy', 'touches', 'field', 'typeid', 'labelid', 'expect', 'expectexact', 'expectid', 'tap', 'tapif', 'tapid', 'tapexact', 'type', 'absent', 'absentid', 'selected', 'tab', 'scrollto', 'dismiss', 'log', 'shot', 'tree', 'audit', 'exec', 'openurl', 'universallink', 'ifid', 'notifications', 'systemexpect', 'systemabsent', 'systemtap', 'systemtree']
173 const commands: SimulatorStep['command'][] = ['launch', 'activate', 'terminate', 'home', 'back', 'return', 'unlabelled', 'background', 'wait', 'delay', 'net', 'swipe', 'drag', 'edge', 'tabbar', 'tapxy', 'touches', 'field', 'typeid', 'labelid', 'valueid', 'sliderid', 'expectapp', 'expect', 'expectexact', 'expectid', 'tap', 'tapif', 'tapid', 'longpressid', 'tapexact', 'type', 'absent', 'absentid', 'selected', 'tab', 'scrollto', 'dismiss', 'log', 'shot', 'tree', 'audit', 'exec', 'openurl', 'universallink', 'ifid', 'notifications', 'systemexpect', 'systemabsent', 'systemtap', 'systemtree']
159174 expect(commands.filter(command => !handled.has(command))).toEqual([])
160175 // ifid re-enters the same dispatcher, so a guarded command is any command.
161176 expect(source).toContain('func run(_ line: String)')
packages/ios/src/testing.tsmodified+3-2
Changes to packages/ios/src/testing.ts
@@ -90,13 +90,13 @@ export type SimulatorStep =
9090 /** One native event record, without quiescence between paths. Times are seconds. */
9191 | { command: 'touches', paths: TimedTouchPoint[][] }
9292 | { command: 'field', index: number, text: string }
93 | { command: 'typeid' | 'labelid', id: string, text: string }
93 | { command: 'typeid' | 'labelid' | 'valueid', id: string, text: string }
9494 /** Adjust an actual native UISlider by identifier, using its normalized range. */
9595 | { command: 'sliderid', id: string, position: number }
9696 /** Assert a handoff reached the foreground application without launching it. */
9797 | { command: 'expectapp', text: string, seconds?: number }
9898 | { command: 'expect' | 'expectexact' | 'expectid' | 'systemexpect', text: string, seconds?: number }
99 | { command: 'tap' | 'tapif' | 'tapid' | 'tapexact' | 'type' | 'absent' | 'absentid' | 'selected' | 'tab' | 'scrollto' | 'dismiss' | 'log' | 'systemtap' | 'systemabsent', text: string }
99 | { command: 'tap' | 'tapif' | 'tapid' | 'longpressid' | 'tapexact' | 'type' | 'absent' | 'absentid' | 'selected' | 'tab' | 'scrollto' | 'dismiss' | 'log' | 'systemtap' | 'systemabsent', text: string }
100100 | { command: 'shot' | 'tree' | 'audit' | 'exec' | 'systemtree', name: string }
101101 | { command: 'openurl', url: string }
102102 /** Use Apple's universalLinksOnly API, allowing association downloads up to 60 seconds. */
@@ -156,6 +156,7 @@ export function serializeSimulatorSteps(steps: readonly SimulatorStep[]): string
156156 else if ('id' in step) arg = `${token(step.id)} ${step.text}`
157157 else if ('text' in step) {
158158 arg = step.text
159 if (step.command === 'longpressid') token(arg)
159160 if (step.command === 'expectapp' && !/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(arg))
160161 throw new Error('Expected an application bundle identifier')
161162 if ('seconds' in step && step.seconds !== undefined) {
packages/ios/templates/testing/DriverTests.swift.templatemodified+18-0
Changes to packages/ios/templates/testing/DriverTests.swift.template
@@ -21,6 +21,7 @@ import Darwin
2121/// tap <text> first hittable element whose label contains text
2222/// tapif <text> tap it when it is on screen, else carry on (the big timer's "Show the controls")
2323/// tapid <identifier>
24/// longpressid <identifier> holds a hittable native element to open its edit menu
2425/// touches <JSON> timed normalized touch paths in one native dispatch
2526/// tapxy <x> <y> normalised 0…1 of the screen
2627/// tapexact <label> a button whose label is exactly this
@@ -36,6 +37,7 @@ import Darwin
3637/// absent <text> logs PASS when no element contains text
3738/// expectid <id> [| secs] / absentid <id> the same by accessibility identifier (testID)
3839/// labelid <id> <text> that element's label is exactly text (a count, a status)
40/// valueid <id> <text> that element's actual string value is exactly text
3941/// tabbar shown|hidden the native tab bar (Today, Calendar, …) can be tapped, or not
4042/// selected <tab> the tab bar's selected button is this one
4143/// tab <name> tap a tab bar button
@@ -292,6 +294,14 @@ final class DriverTests: XCTestCase {
292294 let target = identified(arg)
293295 if target.waitForExistence(timeout: 10) { press(target); note("tapid \(arg): ok") }
294296 else { XCTFail("Element not found"); note("FAIL tapid \(arg): NOT FOUND") }
297 case "longpressid":
298 let target = identified(arg)
299 guard target.waitForExistence(timeout: 10), target.isHittable else {
300 check(false, "longpressid \(arg) is hittable")
301 return
302 }
303 target.press(forDuration: 1.2)
304 note("longpressid \(arg): ok")
295305 case "touches":
296306 do {
297307 guard let data = arg.data(using: .utf8),
@@ -404,6 +414,14 @@ final class DriverTests: XCTestCase {
404414 let want = idParts.count > 1 ? idParts[1] : ""
405415 let seen = target.waitForExistence(timeout: 5) ? target.label : "(missing)"
406416 check(seen == want, "labelid \(idParts[0]) \(want) (saw \(seen))")
417 case "valueid":
418 let idParts = arg.split(separator: " ", maxSplits: 1).map(String.init)
419 let target = identified(idParts[0])
420 let want = idParts.count > 1 ? idParts[1] : ""
421 let matched = poll(5) {
422 target.exists && (target.value as? String) == want ? target : nil
423 }
424 check(matched != nil, "valueid \(idParts[0]) \(want)")
407425 case "absentid":
408426 check(!identified(arg).exists, "absentid \(arg)")
409427 case "absent":