mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
2ec4e91b11
* refactor(core): move the command descriptor registry into its own package `src/core/command-descriptor/`, `src/command-catalog.ts`, `src/core/wait-positionals.ts` and `src/core/parse-timeout.ts` move as git renames into a new private package `@agent-device/command-registry` (deps: contracts, selectors). One subpath per module points straight at the moved file; no `index.ts`, no re-export at the old path. Every consumer switches to the owning specifier. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jqfa11D8QsCMuL17SsLvDz * test(host-kit): pin the command-registry package inside the daemon code graph The daemon reaches the registry and its catalog only by workspace specifier. A walk that stopped at the package boundary would report an unchanged signature after a descriptor edit, and the client would keep reusing a daemon running the superseded policy. The manifest is asserted beside the sources because its `exports` map is what chose them. The cache doc comment quoting the old ~800-module graph is corrected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jqfa11D8QsCMuL17SsLvDz * chore(gates): point the descriptor-registry gates at the package path R66's `COMMAND_DESCRIPTOR_MODULE`, R16's record-runtime join subject and the Fallow `AssertTrue` totality-guard key follow the registry to its package. The two descriptor hubs leave `HUB_ENTRY_FILES` because the package manifest now publishes them, so the eager-closure gate discovers them as facades and one entry gets one rule; this also flips `denyPlatformImplementations` from false (hub) to true (package entry) for both, which is intentional and stricter. `command-registry` joins the ranked spine at rank 1. No `APPROVED_OVER_CEILING` row: rename detection carries every moved entry's merge-base baseline, so all twelve fall under the no-growth rule rather than a ceiling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jqfa11D8QsCMuL17SsLvDz --------- Co-authored-by: Claude <noreply@anthropic.com>
101 lines
3.4 KiB
TypeScript
101 lines
3.4 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import path from 'node:path';
|
|
|
|
import { PUBLIC_COMMANDS } from '@agent-device/command-registry/catalog';
|
|
import {
|
|
type LiveContext,
|
|
runStep,
|
|
verifyBehavior,
|
|
verifyCommand,
|
|
verifyNestedReplayCommand,
|
|
} from './live-harness.ts';
|
|
import {
|
|
assertReplayCommands,
|
|
readReplayCommands,
|
|
replayAttemptTimeoutMs,
|
|
} from '../live-device-e2e/replay-evidence.ts';
|
|
import { runLiveReplayTestSuite } from '../live-device-e2e/replay-suite.ts';
|
|
|
|
const C = PUBLIC_COMMANDS;
|
|
|
|
export async function assertFixtureReplays(context: LiveContext): Promise<void> {
|
|
await runStep(context, 'open Android fixture for helper-backed gesture proof', [
|
|
'open',
|
|
context.appId,
|
|
'--relaunch',
|
|
]);
|
|
const helperSnapshot = await runStep(context, 'inspect Android helper snapshot metadata', [
|
|
'snapshot',
|
|
'-i',
|
|
]);
|
|
assert.equal(
|
|
helperSnapshot.json?.data?.androidSnapshot?.backend,
|
|
'android-helper',
|
|
JSON.stringify(helperSnapshot.json),
|
|
);
|
|
assert.ok(
|
|
typeof helperSnapshot.json?.data?.androidSnapshot?.helperVersion === 'string',
|
|
JSON.stringify(helperSnapshot.json),
|
|
);
|
|
await runStep(context, 'close helper snapshot session before replay', ['close']);
|
|
|
|
const navigationReplay = path.resolve(
|
|
'test/integration/replays/android/fixture/01-navigation-scroll.ad',
|
|
);
|
|
const navigationActions = readReplayCommands(navigationReplay);
|
|
const replay = await runStep(
|
|
context,
|
|
'run Android fixture catalog traversal through replay',
|
|
['replay', navigationReplay, '--timeout', String(replayAttemptTimeoutMs(navigationReplay))],
|
|
{ timeoutMs: replayAttemptTimeoutMs(navigationReplay) + 30_000 },
|
|
);
|
|
assert.equal(replay.json?.data?.replayed, navigationActions.length, JSON.stringify(replay.json));
|
|
assertReplayCommands(navigationReplay, navigationActions, [C.swipe, C.scroll]);
|
|
verifyCommand(
|
|
context,
|
|
C.replay,
|
|
'retry-free fixture catalog traversal completes through public replay',
|
|
);
|
|
verifyNestedReplayCommand(
|
|
context,
|
|
C.swipe,
|
|
C.replay,
|
|
'fixture replay executes direct directional swipe evidence',
|
|
);
|
|
verifyNestedReplayCommand(
|
|
context,
|
|
C.scroll,
|
|
C.replay,
|
|
'fixture replay executes edge-aware bottom and top traversal',
|
|
);
|
|
verifyBehavior(
|
|
context,
|
|
'long-list-scroll-recovery',
|
|
'catalog replay reached its footer then returned to the top landmark without retry',
|
|
);
|
|
|
|
const checkoutReplay = path.resolve('examples/test-app/replays/checkout-form-android.ad');
|
|
const gestureReplay = path.resolve('examples/test-app/replays/gesture-lab-android.ad');
|
|
const dragReplay = path.resolve('examples/test-app/replays/drag-android.ad');
|
|
const { commandsByScript } = await runLiveReplayTestSuite({
|
|
context,
|
|
runStep,
|
|
step: 'run Android fixture suite without retries',
|
|
scripts: [checkoutReplay, gestureReplay, dragReplay],
|
|
});
|
|
assertReplayCommands(gestureReplay, commandsByScript.get(gestureReplay) ?? [], [C.gesture]);
|
|
assertReplayCommands(dragReplay, commandsByScript.get(dragReplay) ?? [], [C.gesture]);
|
|
verifyCommand(context, C.test, 'retry-free Android fixture suite emits non-empty JUnit evidence');
|
|
verifyNestedReplayCommand(
|
|
context,
|
|
C.gesture,
|
|
C.test,
|
|
'Android fixture suite executes planned one- and two-pointer gestures',
|
|
);
|
|
verifyBehavior(
|
|
context,
|
|
'helper-backed-gesture-recovery',
|
|
'gesture fixture observed two-pointer pan, fling, pinch, rotate, and transform outcomes',
|
|
);
|
|
}
|