Skip to content

Commit a906425

Browse files
anupamchughanupamchughthymikee
authored
feat: support precise location settings (#491)
* feat: support precise location settings * fix: address location settings review * chore: refresh fallow baselines for location settings * fix: clean up location settings dispatch --------- Co-authored-by: anupamchugh <anupamchugh@gmail.com> Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
1 parent d2a3742 commit a906425

24 files changed

Lines changed: 583 additions & 339 deletions

File tree

.github/workflows/ios.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ jobs:
5050
preferred-device-name: iPhone 17 Pro
5151

5252
- name: Run iOS simulator smoke replay
53-
run: node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator/01-settings.ad --retries 2 --report-junit test/artifacts/replays-ios-simulator-smoke.junit.xml
53+
run: |
54+
pnpm clean:daemon
55+
node --experimental-strip-types src/bin.ts test test/integration/replays/ios/simulator/01-settings.ad --retries 2 --report-junit test/artifacts/replays-ios-simulator-smoke.junit.xml
5456
5557
- name: Run iOS physical device smoke replay
5658
if: env.IOS_UDID != ''
5759
env:
5860
IOS_UDID: ${{ vars.IOS_UDID }}
59-
run: node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device/01-physical-lifecycle.ad --udid "$IOS_UDID" --retries 2 --report-junit test/artifacts/replays-ios-device-smoke.junit.xml
61+
run: |
62+
pnpm clean:daemon
63+
node --experimental-strip-types src/bin.ts test test/integration/replays/ios/device/01-physical-lifecycle.ad --udid "$IOS_UDID" --retries 2 --report-junit test/artifacts/replays-ios-device-smoke.junit.xml
6064
6165
- name: Upload iOS artifacts
6266
if: always()

.github/workflows/pr-preview.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ concurrency: preview-${{ github.ref }}
1212

1313
jobs:
1414
deploy-preview:
15+
if: github.event.pull_request.head.repo.full_name == github.repository
1516
runs-on: ubuntu-latest
1617
permissions:
1718
contents: write

fallow-baselines/dupes.json

Lines changed: 247 additions & 225 deletions
Large diffs are not rendered by default.

fallow-baselines/health.json

Lines changed: 33 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,14 @@
8888
}
8989
},
9090
"src/cli/commands/output.ts": {
91-
"complexity_critical": {
92-
"count": 2
93-
},
94-
"complexity_high": {
95-
"count": 2
96-
},
97-
"crap_critical": {
98-
"count": 2
91+
"complexity_moderate": {
92+
"count": 1
9993
},
100-
"crap_high": {
101-
"count": 2
94+
"crap_moderate": {
95+
"count": 4
10296
}
10397
},
10498
"src/cli/commands/react-devtools.ts": {
105-
"crap_high": {
106-
"count": 1
107-
},
10899
"crap_moderate": {
109100
"count": 1
110101
}
@@ -118,14 +109,11 @@
118109
"complexity_high": {
119110
"count": 1
120111
},
121-
"complexity_moderate": {
122-
"count": 1
123-
},
124112
"crap_high": {
125113
"count": 1
126114
},
127115
"crap_moderate": {
128-
"count": 4
116+
"count": 3
129117
}
130118
},
131119
"src/client-companion-tunnel.ts": {
@@ -230,7 +218,7 @@
230218
"count": 2
231219
},
232220
"crap_high": {
233-
"count": 3
221+
"count": 4
234222
},
235223
"crap_moderate": {
236224
"count": 5
@@ -256,11 +244,6 @@
256244
"count": 4
257245
}
258246
},
259-
"src/daemon.ts": {
260-
"crap_critical": {
261-
"count": 1
262-
}
263-
},
264247
"src/daemon/__tests__/http-server.test.ts": {
265248
"crap_moderate": {
266249
"count": 1
@@ -441,7 +424,7 @@
441424
"count": 1
442425
},
443426
"crap_high": {
444-
"count": 2
427+
"count": 1
445428
}
446429
},
447430
"src/daemon/handlers/session-perf.ts": {
@@ -532,6 +515,11 @@
532515
"count": 3
533516
}
534517
},
518+
"src/daemon/handlers/snapshot-settings.ts": {
519+
"complexity_moderate": {
520+
"count": 1
521+
}
522+
},
535523
"src/daemon/http-server.ts": {
536524
"complexity_critical": {
537525
"count": 1
@@ -575,7 +563,7 @@
575563
"count": 2
576564
},
577565
"crap_moderate": {
578-
"count": 4
566+
"count": 3
579567
}
580568
},
581569
"src/daemon/runtime-hints.ts": {
@@ -643,14 +631,6 @@
643631
"count": 2
644632
}
645633
},
646-
"src/daemon/transport.ts": {
647-
"crap_high": {
648-
"count": 1
649-
},
650-
"crap_moderate": {
651-
"count": 1
652-
}
653-
},
654634
"src/platforms/android/app-lifecycle.ts": {
655635
"complexity_moderate": {
656636
"count": 1
@@ -688,7 +668,7 @@
688668
}
689669
},
690670
"src/platforms/android/settings.ts": {
691-
"complexity_moderate": {
671+
"complexity_high": {
692672
"count": 1
693673
}
694674
},
@@ -698,18 +678,9 @@
698678
}
699679
},
700680
"src/platforms/android/ui-hierarchy.ts": {
701-
"complexity_critical": {
702-
"count": 1
703-
},
704681
"complexity_high": {
705682
"count": 1
706683
},
707-
"complexity_moderate": {
708-
"count": 1
709-
},
710-
"crap_critical": {
711-
"count": 1
712-
},
713684
"crap_high": {
714685
"count": 1
715686
},
@@ -741,11 +712,6 @@
741712
"count": 1
742713
}
743714
},
744-
"src/platforms/ios/__tests__/perf.test.ts": {
745-
"crap_high": {
746-
"count": 1
747-
}
748-
},
749715
"src/platforms/ios/apps.ts": {
750716
"complexity_high": {
751717
"count": 1
@@ -1035,52 +1001,51 @@
10351001
},
10361002
"runtime_coverage_findings": [],
10371003
"target_keys": [
1038-
"src/daemon/handlers/session.ts:complexity",
10391004
"src/daemon/handlers/snapshot-capture.ts:high impact",
1005+
"src/daemon/handlers/session.ts:complexity",
10401006
"src/daemon/handlers/session-device-utils.ts:high impact",
1041-
"src/utils/process-identity.ts:high impact",
10421007
"src/client-shared.ts:high impact",
10431008
"src/daemon/handlers/session-replay-script.ts:complexity",
1044-
"src/daemon/config.ts:high impact",
10451009
"src/commands/selector-read-utils.ts:high impact",
1046-
"src/utils/args.ts:high impact",
1010+
"src/daemon/config.ts:high impact",
1011+
"src/daemon/handlers/session-replay-heal.ts:complexity",
10471012
"src/daemon/android-snapshot-freshness.ts:high impact",
1048-
"src/daemon/session-store.ts:complexity",
1049-
"src/platforms/boot-diagnostics.ts:complexity",
10501013
"src/cli/commands/connection-runtime.ts:complexity",
1051-
"src/daemon/handlers/session-replay-heal.ts:complexity",
1052-
"src/cli/commands/output.ts:complexity",
1014+
"src/platforms/boot-diagnostics.ts:complexity",
1015+
"src/utils/args.ts:high impact",
1016+
"src/daemon/session-store.ts:complexity",
1017+
"src/daemon/handlers/session-open-target.ts:high impact",
10531018
"src/daemon/handlers/snapshot-alert.ts:complexity",
1054-
"src/cli/commands/connection.ts:untested risk",
1055-
"src/daemon/handlers/session-inventory.ts:complexity",
10561019
"src/utils/success-text.ts:high impact",
1020+
"src/daemon/handlers/session-inventory.ts:complexity",
10571021
"src/daemon/handlers/install-source.ts:complexity",
1058-
"src/daemon/script-utils.ts:high impact",
10591022
"src/utils/snapshot-processing.ts:high impact",
1060-
"src/daemon/handlers/session-open-target.ts:high impact",
1061-
"src/cli.ts:complexity",
1023+
"src/cli/commands/output.ts:untested risk",
1024+
"src/daemon/script-utils.ts:high impact",
1025+
"src/utils/process-identity.ts:high impact",
10621026
"src/utils/output.ts:high impact",
1027+
"src/cli.ts:complexity",
1028+
"src/platforms/ios/xml.ts:high impact",
10631029
"src/daemon/handlers/session-state.ts:complexity",
10641030
"src/daemon/handlers/session-open.ts:complexity",
1065-
"src/platforms/android/ui-hierarchy.ts:high impact",
10661031
"src/daemon/request-cancel.ts:high impact",
10671032
"src/utils/device.ts:high impact",
1068-
"src/platforms/android/input-actions.ts:complexity",
1069-
"src/daemon/app-log-process.ts:high impact",
10701033
"src/utils/snapshot-lines.ts:high impact",
1071-
"src/client-metro.ts:complexity",
1034+
"src/daemon/app-log-process.ts:high impact",
10721035
"src/utils/selector-build.ts:high impact",
1073-
"src/platforms/ios/xml.ts:high impact",
1036+
"src/client-metro.ts:complexity",
1037+
"src/platforms/android/input-actions.ts:complexity",
10741038
"src/utils/text-surface.ts:high impact",
10751039
"src/cli-test.ts:untested risk",
10761040
"src/utils/keyed-lock.ts:high impact",
1077-
"src/daemon/app-log-stream.ts:high impact",
10781041
"src/core/batch.ts:complexity",
10791042
"src/platforms/ios/runner-xctestrun.ts:complexity",
1043+
"src/daemon/app-log-stream.ts:high impact",
10801044
"src/platforms/android/sdk.ts:high impact",
10811045
"src/utils/source-value.ts:high impact",
10821046
"src/utils/screenshot-diff-regions.ts:complexity",
1047+
"src/client-companion-tunnel-worker.ts:complexity",
10831048
"src/daemon/screenshot-overlay.ts:untested risk",
10841049
"src/utils/screenshot-diff-non-text.ts:complexity"
10851050
]
1086-
}
1051+
}

skills/agent-device/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ agent-device help dogfood
3232
Default loop: `open -> snapshot/-i -> get/is/find or press/fill/scroll/wait -> verify -> close`.
3333

3434
Use this skill only to route into version-matched CLI help. Let `help workflow` provide exact command shapes, platform limits, and current workflow guidance.
35+
36+
For precise location workflows, read the installed `settings` help before planning so coordinate support and platform limits come from the active CLI version.

src/__tests__/cli-client-commands.test.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
MetroPrepareOptions,
1313
MetroReloadOptions,
1414
} from '../client.ts';
15+
import type { SettingsUpdateOptions } from '../client-types.ts';
1516
import { AppError } from '../utils/errors.ts';
1617
import { resolveCliOptions } from '../utils/cli-options.ts';
1718

@@ -740,6 +741,82 @@ test('install prints command-owned success output in human mode', async () => {
740741
assert.match(stdout, /Installed: Demo/);
741742
});
742743

744+
test('settings location set forwards coordinates to client settings update', async () => {
745+
let observed: SettingsUpdateOptions | undefined;
746+
const client = createStubClient({
747+
installFromSource: async () => ({
748+
launchTarget: 'com.example.demo',
749+
packageName: 'com.example.demo',
750+
identifiers: { appId: 'com.example.demo' },
751+
}),
752+
updateSettings: async (options) => {
753+
observed = options;
754+
return { identifiers: { session: 'default' } };
755+
},
756+
});
757+
758+
const handled = await tryRunClientBackedCommand({
759+
command: 'settings',
760+
positionals: ['location', 'set', '37.3349', '-122.009'],
761+
flags: {
762+
json: false,
763+
help: false,
764+
version: false,
765+
platform: 'ios',
766+
session: 'maps',
767+
},
768+
client,
769+
});
770+
771+
assert.equal(handled, true);
772+
assert.equal(observed?.platform, 'ios');
773+
assert.equal(observed?.setting, 'location');
774+
assert.equal(observed?.state, 'set');
775+
assert.equal(observed?.latitude, 37.3349);
776+
assert.equal(observed?.longitude, -122.009);
777+
});
778+
779+
test('settings location set rejects invalid coordinates before client call', async () => {
780+
const client = createStubClient({
781+
installFromSource: async () => ({
782+
launchTarget: 'com.example.demo',
783+
packageName: 'com.example.demo',
784+
identifiers: { appId: 'com.example.demo' },
785+
}),
786+
updateSettings: async () => {
787+
throw new Error('unexpected settings update');
788+
},
789+
});
790+
791+
const cases: Array<[string[], RegExp]> = [
792+
[['location', 'set', '91', '-122.009'], /latitude must be a number from -90 to 90/],
793+
[['location', 'set', '37.3349', 'not-a-number'], /longitude must be a number from -180 to 180/],
794+
];
795+
796+
for (const [positionals, message] of cases) {
797+
await assert.rejects(
798+
() =>
799+
tryRunClientBackedCommand({
800+
command: 'settings',
801+
positionals,
802+
flags: {
803+
json: false,
804+
help: false,
805+
version: false,
806+
platform: 'ios',
807+
},
808+
client,
809+
}),
810+
(error: unknown) => {
811+
assert.equal(error instanceof AppError, true);
812+
assert.equal((error as AppError).code, 'INVALID_ARGS');
813+
assert.match((error as AppError).message, message);
814+
return true;
815+
},
816+
);
817+
}
818+
});
819+
743820
async function captureStdout(run: () => Promise<void>): Promise<string> {
744821
let stdout = '';
745822
const originalWrite = process.stdout.write.bind(process.stdout);
@@ -763,6 +840,7 @@ function createStubClient(params: {
763840
reloadMetro?: AgentDeviceClient['metro']['reload'];
764841
open?: AgentDeviceClient['apps']['open'];
765842
screenshot?: AgentDeviceClient['capture']['screenshot'];
843+
updateSettings?: AgentDeviceClient['settings']['update'];
766844
}): AgentDeviceClient {
767845
const unexpectedCommandCall = async (): Promise<never> => {
768846
throw new Error('unexpected command call');
@@ -894,7 +972,9 @@ function createStubClient(params: {
894972
batch: createThrowingMethodGroup<AgentDeviceClient['batch']>(),
895973
observability: createThrowingMethodGroup<AgentDeviceClient['observability']>(),
896974
recording: createThrowingMethodGroup<AgentDeviceClient['recording']>(),
897-
settings: createThrowingMethodGroup<AgentDeviceClient['settings']>(),
975+
settings: {
976+
update: params.updateSettings ?? unexpectedCommandCall,
977+
},
898978
};
899979
}
900980

src/cli/commands/generic.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { announceReplayTestRun } from '../../cli-test.ts';
1212
import { splitSelectorFromArgs } from '../../daemon/selectors.ts';
1313
import { AppError } from '../../utils/errors.ts';
1414
import type { CliFlags } from '../../utils/command-schema.ts';
15+
import { readLocationCoordinate } from '../../utils/location-coordinates.ts';
1516
import { buildSelectionOptions } from './shared.ts';
1617
import { writeCommandCliOutput } from './output.ts';
1718
import type { ClientCommandHandler, ClientCommandHandlerMap } from './router-types.ts';
@@ -471,6 +472,15 @@ function readSettingsOptions(positionals: string[], flags: CliFlags): SettingsUp
471472
) {
472473
return { ...base, setting, state };
473474
}
475+
if (setting === 'location' && state === 'set') {
476+
return {
477+
...base,
478+
setting,
479+
state,
480+
latitude: readLocationCoordinate(positionals[2], 'latitude'),
481+
longitude: readLocationCoordinate(positionals[3], 'longitude'),
482+
};
483+
}
474484
if (setting === 'appearance' && (state === 'light' || state === 'dark' || state === 'toggle')) {
475485
return { ...base, setting, state };
476486
}

0 commit comments

Comments
 (0)