Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/__tests__/cli-client-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ function createStubClient(params: {
identifiers: { appId: 'com.example.demo' },
}),
installFromSource: params.installFromSource,
list: async () => [],
open:
params.open ??
(async () => ({
Expand Down
24 changes: 24 additions & 0 deletions src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,30 @@ test('apps.installFromSource derives Android launchTarget from packageName when
});
});

test('apps.list forwards filters and returns daemon app names', async () => {
const setup = createTransport(async () => ({
ok: true,
data: {
apps: ['Settings (com.apple.Preferences)', 'Demo (com.example.demo)', { ignored: true }],
},
}));
const client = createAgentDeviceClient(setup.config, { transport: setup.transport });

const apps = await client.apps.list({
platform: 'ios',
device: 'iPhone 16',
appsFilter: 'user-installed',
});

assert.equal(setup.calls.length, 1);
assert.equal(setup.calls[0]?.command, 'apps');
assert.deepEqual(setup.calls[0]?.positionals, []);
assert.equal(setup.calls[0]?.flags?.platform, 'ios');
assert.equal(setup.calls[0]?.flags?.device, 'iPhone 16');
assert.equal(setup.calls[0]?.flags?.appsFilter, 'user-installed');
assert.deepEqual(apps, ['Settings (com.apple.Preferences)', 'Demo (com.example.demo)']);
});

test('materializations.release forwards materialization identity through the daemon request', async () => {
const setup = createTransport(async () => ({
ok: true,
Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/close-remote-metro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ test('close with remote-config stops the managed Metro companion for that projec
installFromSource: async () => {
throw new Error('unexpected call');
},
list: async () => {
throw new Error('unexpected call');
},
open: async () => {
throw new Error('unexpected call');
},
Expand Down Expand Up @@ -153,6 +156,9 @@ test('close with remote-config still stops the managed Metro companion when clos
installFromSource: async () => {
throw new Error('unexpected call');
},
list: async () => {
throw new Error('unexpected call');
},
open: async () => {
throw new Error('unexpected call');
},
Expand Down Expand Up @@ -251,6 +257,9 @@ test('close app with remote-config stops the managed Metro companion for that se
installFromSource: async () => {
throw new Error('unexpected call');
},
list: async () => {
throw new Error('unexpected call');
},
open: async () => {
throw new Error('unexpected call');
},
Expand Down Expand Up @@ -348,6 +357,9 @@ test('close with remote-config still succeeds when the config file is gone befor
installFromSource: async () => {
throw new Error('unexpected call');
},
list: async () => {
throw new Error('unexpected call');
},
open: async () => {
throw new Error('unexpected call');
},
Expand Down
1 change: 1 addition & 0 deletions src/client-normalizers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export function buildFlags(options: InternalRequestOptions): CommandFlags {
snapshotScope: options.scope,
snapshotRaw: options.raw,
overlayRefs: options.overlayRefs,
appsFilter: options.appsFilter,
verbose: options.debug,
}) as CommandFlags;
}
Expand Down
7 changes: 7 additions & 0 deletions src/client-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ export type AppInstallFromSourceResult = {
identifiers: AgentDeviceIdentifiers;
};

export type AppListOptions = AgentDeviceRequestOverrides &
AgentDeviceSelectionOptions & {
appsFilter?: 'all' | 'user-installed';
};

export type MaterializationReleaseOptions = AgentDeviceRequestOverrides & {
materializationId: string;
};
Expand Down Expand Up @@ -295,6 +300,7 @@ export type InternalRequestOptions = AgentDeviceClientConfig &
depth?: number;
scope?: string;
raw?: boolean;
appsFilter?: 'all' | 'user-installed';
installSource?: DaemonInstallSource;
retainMaterializedPaths?: boolean;
materializedPathRetentionMs?: number;
Expand Down Expand Up @@ -322,6 +328,7 @@ export type AgentDeviceClient = {
installFromSource: (
options: AppInstallFromSourceOptions,
) => Promise<AppInstallFromSourceResult>;
list: (options?: AppListOptions) => Promise<string[]>;
open: (options: AppOpenOptions) => Promise<AppOpenResult>;
close: (options?: AppCloseOptions) => Promise<AppCloseResult>;
};
Expand Down
8 changes: 8 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
AppCloseOptions,
AppDeployOptions,
AppInstallFromSourceOptions,
AppListOptions,
AppOpenOptions,
CaptureScreenshotOptions,
CaptureSnapshotOptions,
Expand Down Expand Up @@ -142,6 +143,12 @@ export function createAgentDeviceClient(
}),
resolveSessionName(config.session, options.session),
),
list: async (options: AppListOptions = {}) => {
const data = await execute('apps', [], options);
return Array.isArray(data.apps)
? data.apps.filter((app): app is string => typeof app === 'string')
: [];
},
open: async (options: AppOpenOptions) => {
const session = resolveSessionName(config.session, options.session);
const positionals = options.url ? [options.app, options.url] : [options.app];
Expand Down Expand Up @@ -268,6 +275,7 @@ export type {
AppDeployResult,
AppInstallFromSourceOptions,
AppInstallFromSourceResult,
AppListOptions,
AppOpenOptions,
AppOpenResult,
CaptureScreenshotOptions,
Expand Down
5 changes: 4 additions & 1 deletion src/utils/__tests__/cli-option-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ test('remote config schema stays aligned with CLI option metadata', () => {
assert.equal(definition.type, field.type);
assert.equal(definition.min, 'min' in field ? field.min : undefined);
assert.equal(definition.max, 'max' in field ? field.max : undefined);
assert.deepEqual(definition.enumValues ?? [], 'enumValues' in field ? field.enumValues ?? [] : []);
assert.deepEqual(
definition.enumValues ?? [],
'enumValues' in field ? (field.enumValues ?? []) : [],
);
}
});

Expand Down
1 change: 1 addition & 0 deletions website/docs/docs/client-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const client = createAgentDeviceClient({
});

const devices = await client.devices.list({ platform: 'ios' });
const apps = await client.apps.list({ platform: 'ios', appsFilter: 'user-installed' });
const ensured = await client.simulators.ensure({
device: 'iPhone 16',
boot: true,
Expand Down
Loading