peachsim 0.1.1619 → 0.1.1625
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -5
- package/cli/app-fonts.ts +2 -1
- package/cli/bridge-flow-runner.ts +222 -175
- package/cli/cloud-client.ts +81 -48
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +108 -43
- package/cli/commands/inspect/settling.ts +57 -20
- package/cli/commands/maestro-compare.ts +11 -32
- package/cli/commands/platform.ts +74 -17
- package/cli/commands/record.ts +20 -15
- package/cli/commands/upload.ts +5 -1
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/playwright-sim-host.ts +18 -6
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +24 -0
- package/cli/outbound-endpoints.ts +27 -8
- package/cli/parent-pid.ts +0 -9
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +133 -98
- package/detox/motion-change.cjs +37 -0
- package/detox/motion-change.d.cts +16 -0
- package/detox/navigation-lifecycle.ts +22 -4
- package/dist-lib/agent-daemon-client.cjs +3 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +3 -2
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +1528 -937
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +4 -3
- package/dist-lib/cloud.mjs +4 -3
- package/dist-lib/config.cjs +5 -4
- package/dist-lib/config.mjs +5 -4
- package/dist-lib/detox/index.cjs +1021 -364
- package/dist-lib/dev-bundle-resolution.cjs +51 -23
- package/dist-lib/expo-swift-bundle.cjs +1351 -804
- package/dist-lib/expo-swift-metadata.cjs +5 -4
- package/dist-lib/expo-swift-metadata.mjs +5 -4
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +2475 -1343
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1269 -300
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +3 -2
- package/dist-lib/menu.mjs +3 -2
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +5 -4
- package/dist-lib/metro-production-bundle.mjs +5 -4
- package/dist-lib/metro.cjs +806 -398
- package/dist-lib/native-app-identity-project.cjs +729 -21
- package/dist-lib/native-app-identity-project.mjs +728 -21
- package/dist-lib/native-app-identity.cjs +232 -9
- package/dist-lib/native-app-identity.mjs +230 -8
- package/dist-lib/native-firebase-metadata.cjs +5 -4
- package/dist-lib/native-firebase-metadata.mjs +5 -4
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +3 -2
- package/dist-lib/react-native-host-modules.cjs +4 -1
- package/dist-lib/react-native-host-modules.mjs +4 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +94 -28
- package/dist-lib/sdk.cjs +89 -24
- package/dist-lib/sdk.mjs +89 -24
- package/dist-lib/skills.cjs +4471 -2394
- package/dist-lib/swift-compile.cjs +336 -145
- package/dist-lib/swift-compile.mjs +350 -150
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +891 -557
- package/dist-lib/vite.cjs +68 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +33 -13
- package/src/admitted-mac-build.ts +34 -8
- package/src/android-app-identity.ts +146 -0
- package/src/android-autolinking.ts +75 -0
- package/src/android-string-value.ts +36 -0
- package/src/android-strings.ts +5 -36
- package/src/androidAutolinkingContract.ts +40 -0
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/cloud.ts +2 -2
- package/src/codex-client.ts +3 -2
- package/src/dev-bundle-resolution.ts +58 -25
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +477 -173
- package/src/expoSwiftContract.ts +6 -0
- package/src/host/bridge-host.ts +7 -55
- package/src/host/staged-host-files.ts +29 -2
- package/src/installed-package-versions.ts +54 -14
- package/src/launchMetadataContract.ts +24 -0
- package/src/metro-plugin.ts +37 -42
- package/src/native-app-identity-project.ts +118 -3
- package/src/native-app-identity.ts +103 -11
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +90 -60
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-host-modules.ts +3 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-compile.ts +185 -31
- package/src/swift-distribution.lock.json +37 -11
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +58 -27
- package/src/swift-objc-standin.ts +13 -0
- package/src/swift-standin-emit.ts +1 -1
- package/src/swift-standins.ts +17 -5
- package/src/vite-plugin.ts +8 -19
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
package/detox/index.ts
CHANGED
|
@@ -378,7 +378,7 @@ export async function waitForSootsimShellReady(
|
|
|
378
378
|
async function waitForSootsimWorkletsIdle(page: Page, timeout = 3000): Promise<void> {
|
|
379
379
|
const result = await page.evaluate(
|
|
380
380
|
async ({ timeout }) => {
|
|
381
|
-
const getPeerStats =
|
|
381
|
+
const getPeerStats = window.__sootsimTest?.getPeerStats
|
|
382
382
|
if (typeof getPeerStats !== 'function') {
|
|
383
383
|
return { settled: true, skipped: true }
|
|
384
384
|
}
|
|
@@ -387,7 +387,9 @@ async function waitForSootsimWorkletsIdle(page: Page, timeout = 3000): Promise<v
|
|
|
387
387
|
const start = Date.now()
|
|
388
388
|
const deadline = start + timeout
|
|
389
389
|
const requiredStableMs = 120
|
|
390
|
-
let lastPending: number | null = null
|
|
390
|
+
let lastPending: number | null | undefined = null
|
|
391
|
+
let completedReads = 0
|
|
392
|
+
let lastReply: string | null = null
|
|
391
393
|
|
|
392
394
|
while (Date.now() < deadline) {
|
|
393
395
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
@@ -404,6 +406,8 @@ async function waitForSootsimWorkletsIdle(page: Page, timeout = 3000): Promise<v
|
|
|
404
406
|
})
|
|
405
407
|
if (stats === undefined) break
|
|
406
408
|
if (stats === null) return { settled: true, elapsed: Date.now() - start }
|
|
409
|
+
completedReads++
|
|
410
|
+
lastReply = JSON.stringify({ elapsedMs: Date.now() - start, stats })
|
|
407
411
|
lastPending = stats.pendingRunWorklets
|
|
408
412
|
// the runtime records its last drain. bridge latency cannot erase an
|
|
409
413
|
// idle interval or turn a missing stats reply into a zero count.
|
|
@@ -421,42 +425,80 @@ async function waitForSootsimWorkletsIdle(page: Page, timeout = 3000): Promise<v
|
|
|
421
425
|
await sleep(25)
|
|
422
426
|
}
|
|
423
427
|
|
|
424
|
-
|
|
425
|
-
//
|
|
426
|
-
|
|
428
|
+
const waited = Date.now() - start
|
|
429
|
+
// freeze the completed observations before the separate realm probes.
|
|
430
|
+
// a later reply cannot replace what the original deadline observed.
|
|
431
|
+
const observation = { completedReads, lastReply }
|
|
432
|
+
// no reply at all: ask each realm alone, and time this page's own timer,
|
|
433
|
+
// so a silent tenant, a silent shell and a starved page read differently
|
|
434
|
+
let realms: string | null = null
|
|
427
435
|
if (lastPending === null) {
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
+
const probe = async (bridge: unknown) => {
|
|
437
|
+
if (
|
|
438
|
+
!bridge ||
|
|
439
|
+
typeof bridge !== 'object' ||
|
|
440
|
+
!('callTestBridge' in bridge) ||
|
|
441
|
+
typeof bridge.callTestBridge !== 'function'
|
|
442
|
+
) {
|
|
443
|
+
return { status: 'missing-bridge' }
|
|
444
|
+
}
|
|
445
|
+
const callTestBridge = bridge.callTestBridge.bind(bridge)
|
|
446
|
+
const asked = Date.now()
|
|
447
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
448
|
+
try {
|
|
449
|
+
return await Promise.race([
|
|
450
|
+
Promise.resolve()
|
|
451
|
+
.then(() => callTestBridge('getPeerStats', false))
|
|
452
|
+
.then(
|
|
453
|
+
(stats) => ({
|
|
454
|
+
status: 'answered',
|
|
455
|
+
elapsedMs: Date.now() - asked,
|
|
456
|
+
stats,
|
|
457
|
+
}),
|
|
458
|
+
(error: unknown) => ({
|
|
459
|
+
status: 'rejected',
|
|
460
|
+
elapsedMs: Date.now() - asked,
|
|
461
|
+
error: error instanceof Error ? error.message : String(error),
|
|
462
|
+
}),
|
|
463
|
+
),
|
|
464
|
+
new Promise<{ status: 'no-reply'; elapsedMs: number }>((resolve) => {
|
|
465
|
+
timer = setTimeout(
|
|
466
|
+
() => resolve({ status: 'no-reply', elapsedMs: Date.now() - asked }),
|
|
467
|
+
1000,
|
|
468
|
+
)
|
|
469
|
+
}),
|
|
436
470
|
])
|
|
437
|
-
|
|
471
|
+
} finally {
|
|
472
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const timerAsked = Date.now()
|
|
476
|
+
const [tenant, shell, pageTimerLateMs] = await Promise.all([
|
|
477
|
+
probe(window.__sootsimRenderHost),
|
|
478
|
+
probe(window.SootSim?.bridges?.shellHost),
|
|
479
|
+
sleep(0).then(() => Date.now() - timerAsked),
|
|
480
|
+
])
|
|
481
|
+
realms = JSON.stringify({
|
|
482
|
+
phase: 'after-deadline',
|
|
483
|
+
tenant,
|
|
484
|
+
shell,
|
|
485
|
+
pageTimerLateMs,
|
|
486
|
+
})
|
|
438
487
|
}
|
|
439
488
|
return {
|
|
440
489
|
settled: false,
|
|
441
|
-
elapsed:
|
|
490
|
+
elapsed: waited,
|
|
442
491
|
pending: lastPending,
|
|
443
|
-
|
|
492
|
+
realms,
|
|
493
|
+
observation,
|
|
444
494
|
}
|
|
445
495
|
},
|
|
446
496
|
{ timeout },
|
|
447
497
|
)
|
|
448
498
|
if (!result?.settled) {
|
|
449
|
-
const pending =
|
|
450
|
-
result?.pending ??
|
|
451
|
-
`no peer stats reply; shell alone ${
|
|
452
|
-
result?.shellAnswered == null
|
|
453
|
-
? 'has no bridge'
|
|
454
|
-
: result.shellAnswered
|
|
455
|
-
? 'answered within 1000ms'
|
|
456
|
-
: 'silent for 1000ms'
|
|
457
|
-
}`
|
|
499
|
+
const pending = result?.pending ?? `no peer stats reply; ${result?.realms}`
|
|
458
500
|
throw new Error(
|
|
459
|
-
`sootsim worklet runtime did not settle after ${result?.elapsed ?? timeout}ms (pending=${pending})`,
|
|
501
|
+
`sootsim worklet runtime did not settle after ${result?.elapsed ?? timeout}ms (pending=${pending}; observation=${JSON.stringify(result?.observation)})`,
|
|
460
502
|
)
|
|
461
503
|
}
|
|
462
504
|
}
|
|
@@ -756,67 +798,62 @@ async function nodeOffsetToPage(
|
|
|
756
798
|
return sootsimToPage(page, center.x + dx, center.y + dy)
|
|
757
799
|
}
|
|
758
800
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
)
|
|
767
|
-
const center =
|
|
768
|
-
resolvedTarget &&
|
|
769
|
-
typeof resolvedTarget.cx === 'number' &&
|
|
770
|
-
typeof resolvedTarget.cy === 'number'
|
|
771
|
-
? { x: resolvedTarget.cx, y: resolvedTarget.cy }
|
|
772
|
-
: getNodeCenter(nodeInfo)
|
|
773
|
-
const frame = nodeInfo.visibleFrame ?? {
|
|
774
|
-
x: nodeInfo.absolutePosition?.x ?? nodeInfo.layout?.x ?? 0,
|
|
775
|
-
y: nodeInfo.absolutePosition?.y ?? nodeInfo.layout?.y ?? 0,
|
|
776
|
-
width: nodeInfo.layout?.width ?? 0,
|
|
777
|
-
height: nodeInfo.layout?.height ?? 0,
|
|
778
|
-
}
|
|
779
|
-
const viewport = await readSootsimInteractiveViewport(page)
|
|
780
|
-
const fullFrameTolerance = 0.5
|
|
781
|
-
// the resolved center is the matched frame's center unless the frame is clipped.
|
|
782
|
-
const canTapCenter =
|
|
783
|
-
typeof nodeInfo.absolutePosition?.x === 'number' &&
|
|
784
|
-
typeof nodeInfo.absolutePosition?.y === 'number' &&
|
|
785
|
-
typeof nodeInfo.layout?.width === 'number' &&
|
|
786
|
-
typeof nodeInfo.layout?.height === 'number' &&
|
|
787
|
-
Math.abs(frame.x - nodeInfo.absolutePosition.x) <= fullFrameTolerance &&
|
|
788
|
-
Math.abs(frame.y - nodeInfo.absolutePosition.y) <= fullFrameTolerance &&
|
|
789
|
-
frame.width >= nodeInfo.layout.width - fullFrameTolerance &&
|
|
790
|
-
frame.height >= nodeInfo.layout.height - fullFrameTolerance &&
|
|
791
|
-
center.x >= 0 &&
|
|
792
|
-
center.y >= 0 &&
|
|
793
|
-
center.x < viewport.width &&
|
|
794
|
-
center.y < viewport.height
|
|
795
|
-
if (canTapCenter) return center
|
|
796
|
-
|
|
797
|
-
const left = Math.max(0, frame.x)
|
|
798
|
-
const top = Math.max(0, frame.y)
|
|
799
|
-
const right = Math.min(viewport.width, frame.x + frame.width)
|
|
800
|
-
const bottom = Math.min(viewport.height, frame.y + frame.height)
|
|
801
|
-
|
|
802
|
-
if (right <= left || bottom <= top) {
|
|
803
|
-
return center
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
return {
|
|
807
|
-
x: left + (right - left) / 2,
|
|
808
|
-
y: top + (bottom - top) / 2,
|
|
809
|
-
}
|
|
810
|
-
}
|
|
801
|
+
type TapPoint =
|
|
802
|
+
| { x: number; y: number }
|
|
803
|
+
| {
|
|
804
|
+
nodeId: number
|
|
805
|
+
viewport: { width: number; height: number }
|
|
806
|
+
offset?: { x?: number; y?: number }
|
|
807
|
+
}
|
|
811
808
|
|
|
812
|
-
async function dispatchTapAtSootsimPoint(
|
|
813
|
-
page: Page,
|
|
814
|
-
x: number,
|
|
815
|
-
y: number,
|
|
816
|
-
token?: string,
|
|
817
|
-
) {
|
|
809
|
+
async function dispatchTapAtSootsimPoint(page: Page, point: TapPoint, token?: string) {
|
|
818
810
|
await page.evaluate(
|
|
819
|
-
async ({
|
|
811
|
+
async ({ point, token }) => {
|
|
812
|
+
let x: number
|
|
813
|
+
let y: number
|
|
814
|
+
if ('nodeId' in point) {
|
|
815
|
+
// resolve after the viewport read, in the browser task that dispatches
|
|
816
|
+
// the pointer. a host round trip here can outlive a startup relayout.
|
|
817
|
+
const resolved = await window.__sootsimTest!.resolveTapTarget(point.nodeId)
|
|
818
|
+
if (!resolved) throw new Error(`element disappeared before tap: ${point.nodeId}`)
|
|
819
|
+
const node = resolved.match
|
|
820
|
+
if (point.offset) {
|
|
821
|
+
x = node.absolutePosition.x + (point.offset.x ?? node.layout.width / 2)
|
|
822
|
+
y = node.absolutePosition.y + (point.offset.y ?? node.layout.height / 2)
|
|
823
|
+
} else {
|
|
824
|
+
x = resolved.cx
|
|
825
|
+
y = resolved.cy
|
|
826
|
+
const frame = node.visibleFrame ?? {
|
|
827
|
+
x: node.absolutePosition.x,
|
|
828
|
+
y: node.absolutePosition.y,
|
|
829
|
+
width: node.layout.width,
|
|
830
|
+
height: node.layout.height,
|
|
831
|
+
}
|
|
832
|
+
const { viewport } = point
|
|
833
|
+
const tolerance = 0.5
|
|
834
|
+
const canTapCenter =
|
|
835
|
+
Math.abs(frame.x - node.absolutePosition.x) <= tolerance &&
|
|
836
|
+
Math.abs(frame.y - node.absolutePosition.y) <= tolerance &&
|
|
837
|
+
frame.width >= node.layout.width - tolerance &&
|
|
838
|
+
frame.height >= node.layout.height - tolerance &&
|
|
839
|
+
x >= 0 &&
|
|
840
|
+
y >= 0 &&
|
|
841
|
+
x < viewport.width &&
|
|
842
|
+
y < viewport.height
|
|
843
|
+
if (!canTapCenter) {
|
|
844
|
+
const left = Math.max(0, frame.x)
|
|
845
|
+
const top = Math.max(0, frame.y)
|
|
846
|
+
const right = Math.min(viewport.width, frame.x + frame.width)
|
|
847
|
+
const bottom = Math.min(viewport.height, frame.y + frame.height)
|
|
848
|
+
if (right > left && bottom > top) {
|
|
849
|
+
x = left + (right - left) / 2
|
|
850
|
+
y = top + (bottom - top) / 2
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
} else {
|
|
855
|
+
;({ x, y } = point)
|
|
856
|
+
}
|
|
820
857
|
const interactTap = window.SootSim?.bridges?.interact?.tap
|
|
821
858
|
if (typeof interactTap === 'function') {
|
|
822
859
|
if (token) window.__sootsimAnchorTimedInput?.(token)
|
|
@@ -832,7 +869,7 @@ async function dispatchTapAtSootsimPoint(
|
|
|
832
869
|
if (token) window.__sootsimAnchorTimedInput?.(token)
|
|
833
870
|
await tap(x, y)
|
|
834
871
|
},
|
|
835
|
-
{
|
|
872
|
+
{ point, token },
|
|
836
873
|
)
|
|
837
874
|
}
|
|
838
875
|
|
|
@@ -1087,17 +1124,13 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
|
|
|
1087
1124
|
`Cannot perform action due to constraint failure: element not visible (${describeNodeVisibility(node)}): ${JSON.stringify(matcher)}`,
|
|
1088
1125
|
)
|
|
1089
1126
|
}
|
|
1090
|
-
const
|
|
1091
|
-
const targetSootSimX =
|
|
1092
|
-
typeof point?.x === 'number'
|
|
1093
|
-
? node.absolutePosition.x + point.x
|
|
1094
|
-
: (defaultPoint?.x ?? node.absolutePosition.x + node.layout.width / 2)
|
|
1095
|
-
const targetSootSimY =
|
|
1096
|
-
typeof point?.y === 'number'
|
|
1097
|
-
? node.absolutePosition.y + point.y
|
|
1098
|
-
: (defaultPoint?.y ?? node.absolutePosition.y + node.layout.height / 2)
|
|
1127
|
+
const viewport = await readSootsimInteractiveViewport(page)
|
|
1099
1128
|
const captureToken = (page as any).__sootsimCaptureToken
|
|
1100
|
-
await dispatchTapAtSootsimPoint(
|
|
1129
|
+
await dispatchTapAtSootsimPoint(
|
|
1130
|
+
page,
|
|
1131
|
+
{ nodeId: node.nodeId, viewport, offset: point },
|
|
1132
|
+
captureToken,
|
|
1133
|
+
)
|
|
1101
1134
|
if (_synchronizationEnabled) {
|
|
1102
1135
|
await waitForSootsimIdle(page)
|
|
1103
1136
|
}
|
|
@@ -1540,8 +1573,10 @@ function createSootSimElement(matcher: Matcher): SootSimElement {
|
|
|
1540
1573
|
const captureToken = (page as any).__sootsimCaptureToken
|
|
1541
1574
|
await dispatchTapAtSootsimPoint(
|
|
1542
1575
|
page,
|
|
1543
|
-
|
|
1544
|
-
|
|
1576
|
+
{
|
|
1577
|
+
x: tapTarget.target.absolutePosition.x + tapTarget.target.layout.width / 2,
|
|
1578
|
+
y: tapTarget.target.absolutePosition.y + tapTarget.target.layout.height / 2,
|
|
1579
|
+
},
|
|
1545
1580
|
captureToken,
|
|
1546
1581
|
)
|
|
1547
1582
|
},
|
package/detox/motion-change.cjs
CHANGED
|
@@ -102,6 +102,42 @@ function measureProofMotionChange(input) {
|
|
|
102
102
|
})
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// a target sampled at every display frame (each gap at most one 60fps interval)
|
|
106
|
+
// legitimately includes held input and settled frames whose pairs show no
|
|
107
|
+
// change. such a sequence proves motion only when at least six pairs (100ms of
|
|
108
|
+
// display frames) clear the minimum: a one-frame flash moves two pairs and a
|
|
109
|
+
// static capture none, and both fail. a sparser sequence samples distinct
|
|
110
|
+
// stages, so each pair must clear it.
|
|
111
|
+
const DENSE_SAMPLE_MAX_GAP_MS = 1000 / 60 + 1
|
|
112
|
+
const DENSE_SAMPLE_MINIMUM_MOVING_PAIRS = 6
|
|
113
|
+
|
|
114
|
+
function evaluateMotionTransitions(transitions) {
|
|
115
|
+
const measured = transitions.every(
|
|
116
|
+
(transition) =>
|
|
117
|
+
transition.dimensionsMatch &&
|
|
118
|
+
transition.changePercent !== null &&
|
|
119
|
+
transition.changePixels !== null &&
|
|
120
|
+
transition.totalPixels !== null,
|
|
121
|
+
)
|
|
122
|
+
const moving = transitions.filter((transition) => transition.passedMinimum).length
|
|
123
|
+
const dense =
|
|
124
|
+
transitions.length > DENSE_SAMPLE_MINIMUM_MOVING_PAIRS &&
|
|
125
|
+
transitions.every(
|
|
126
|
+
(transition) =>
|
|
127
|
+
transition.toOffsetMs - transition.fromOffsetMs <= DENSE_SAMPLE_MAX_GAP_MS,
|
|
128
|
+
)
|
|
129
|
+
return {
|
|
130
|
+
dense,
|
|
131
|
+
moving,
|
|
132
|
+
passed:
|
|
133
|
+
transitions.length > 0 &&
|
|
134
|
+
measured &&
|
|
135
|
+
(dense
|
|
136
|
+
? moving >= DENSE_SAMPLE_MINIMUM_MOVING_PAIRS
|
|
137
|
+
: moving === transitions.length),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
105
141
|
const DISPLAY_LINK_INTERVAL_MS = 1000 / 60
|
|
106
142
|
const DISCRETE_FRAME_TIMING_TOLERANCE_MS = 1000 / 60 + 1000 / 600
|
|
107
143
|
|
|
@@ -361,6 +397,7 @@ module.exports = {
|
|
|
361
397
|
MOTION_CHANGE_DEFINITION,
|
|
362
398
|
measureMotionChange,
|
|
363
399
|
measureProofMotionChange,
|
|
400
|
+
evaluateMotionTransitions,
|
|
364
401
|
DISCRETE_FRAME_TIMING_TOLERANCE_MS,
|
|
365
402
|
parseDiscreteFrameCadence,
|
|
366
403
|
classifyDiscreteFrame,
|
|
@@ -34,6 +34,22 @@ export function measureProofMotionChange(
|
|
|
34
34
|
input: Omit<MotionChangeInput, 'definition' | 'installGlobal'>,
|
|
35
35
|
): MotionChangeMeasurement
|
|
36
36
|
|
|
37
|
+
export type MotionTransitionSample = {
|
|
38
|
+
changePercent: number | null
|
|
39
|
+
changePixels: number | null
|
|
40
|
+
dimensionsMatch: boolean
|
|
41
|
+
fromOffsetMs: number
|
|
42
|
+
passedMinimum: boolean
|
|
43
|
+
toOffsetMs: number
|
|
44
|
+
totalPixels: number | null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function evaluateMotionTransitions(transitions: MotionTransitionSample[]): {
|
|
48
|
+
dense: boolean
|
|
49
|
+
moving: number
|
|
50
|
+
passed: boolean
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
export type DiscreteFrameAsset = {
|
|
38
54
|
fixturePath: string
|
|
39
55
|
fixtureSha256: string
|
|
@@ -83,12 +83,28 @@ const activeNavigations = new WeakMap<Page, ActiveNavigation>()
|
|
|
83
83
|
const lastNavigations = new WeakMap<Page, ActiveNavigation>()
|
|
84
84
|
let nextNavigationIdentity = 0
|
|
85
85
|
|
|
86
|
+
const persistedDiagnostics = new WeakSet<PageDiagnostic>()
|
|
87
|
+
|
|
88
|
+
function persistDiagnostic(diagnostic: PageDiagnostic) {
|
|
89
|
+
if (persistedDiagnostics.has(diagnostic)) return
|
|
90
|
+
persistedDiagnostics.add(diagnostic)
|
|
91
|
+
writeAttemptEvent(diagnostic)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// a run that dies at the launch marker never reaches the failure message that
|
|
95
|
+
// prints this buffer, so a guest error writes the console tail that led to it
|
|
96
|
+
// into events.jsonl. passing runs emit no guest error and write nothing.
|
|
86
97
|
function recordPageDiagnostic(page: Page, diagnostic: PageDiagnostic, persist = false) {
|
|
87
98
|
const entries = pageDiagnostics.get(page)
|
|
88
99
|
if (!entries) return
|
|
89
100
|
entries.push(diagnostic)
|
|
90
101
|
if (entries.length > DIAGNOSTIC_LIMIT) entries.shift()
|
|
91
|
-
if (persist)
|
|
102
|
+
if (persist) persistDiagnostic(diagnostic)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function recordGuestError(page: Page, diagnostic: PageDiagnostic) {
|
|
106
|
+
recordPageDiagnostic(page, diagnostic)
|
|
107
|
+
for (const entry of pageDiagnostics.get(page) ?? []) persistDiagnostic(entry)
|
|
92
108
|
}
|
|
93
109
|
|
|
94
110
|
export function isTopLevelDocumentRequest(page: Page, request: Request) {
|
|
@@ -147,15 +163,17 @@ function attachPageDiagnostics(page: Page) {
|
|
|
147
163
|
if (pageDiagnostics.has(page)) return
|
|
148
164
|
pageDiagnostics.set(page, [])
|
|
149
165
|
page.on('console', (message) => {
|
|
150
|
-
|
|
166
|
+
const diagnostic: PageDiagnostic = {
|
|
151
167
|
capturedAt: new Date().toISOString(),
|
|
152
168
|
level: message.type(),
|
|
153
169
|
message: message.text(),
|
|
154
170
|
type: 'page-console',
|
|
155
|
-
}
|
|
171
|
+
}
|
|
172
|
+
if (message.type() === 'error') recordGuestError(page, diagnostic)
|
|
173
|
+
else recordPageDiagnostic(page, diagnostic)
|
|
156
174
|
})
|
|
157
175
|
page.on('pageerror', (error) => {
|
|
158
|
-
|
|
176
|
+
recordGuestError(page, {
|
|
159
177
|
capturedAt: new Date().toISOString(),
|
|
160
178
|
message: error.message,
|
|
161
179
|
type: 'page-error',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -59,7 +59,8 @@ var peachPublicBrand = Object.freeze({
|
|
|
59
59
|
packageName: "peachsim",
|
|
60
60
|
commandName: "peach",
|
|
61
61
|
domain: "peachsim.com",
|
|
62
|
-
origin: "https://peachsim.com"
|
|
62
|
+
origin: "https://peachsim.com",
|
|
63
|
+
boxOrigin: "https://rnx-box.contrast.dev"
|
|
63
64
|
});
|
|
64
65
|
var rnxPublicBrand = peachPublicBrand;
|
|
65
66
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -69,7 +69,8 @@ var peachPublicBrand = Object.freeze({
|
|
|
69
69
|
packageName: "peachsim",
|
|
70
70
|
commandName: "peach",
|
|
71
71
|
domain: "peachsim.com",
|
|
72
|
-
origin: "https://peachsim.com"
|
|
72
|
+
origin: "https://peachsim.com",
|
|
73
|
+
boxOrigin: "https://rnx-box.contrast.dev"
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
// src/auth/shared-session.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/beta.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/beta.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! peach v0.1.
|
|
1
|
+
/*! peach v0.1.1625 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|