Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ jest.mock('../../utils/argentEvents', () => ({
startArgentEventCollectionAsync: jest.fn(),
}));
jest.mock('../../utils/remoteDeviceRunSession', () => ({
createServeSimLaunchInputProviders: jest.fn(() => []),
describeServeSimLaunch: jest.fn(() => null),
parseServeSimLaunchInputs: jest.fn(() => ({ launchArgs: [] })),
ensureFfmpegInstalledAsync: jest.fn(),
getDeviceRunSessionIdOrThrow: jest.fn(),
getNgrokAuthtokenOrThrow: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ import { pollAgentDeviceArtifactsForUploadAsync } from '../utils/agentDeviceArti
import { startAgentDeviceEventCollectionAsync } from '../utils/agentDeviceEvents';
import {
type DetachedProcessHandle,
createServeSimLaunchInputProviders,
describeServeSimLaunch,
getDeviceRunSessionIdOrThrow,
getNgrokAuthtokenOrThrow,
getNgrokTunnelDomainOrThrow,
parseServeSimLaunchInputs,
selectXcodeDeveloperDirectoryAsync,
spawnDetached,
startNgrokTunnelAsync,
Expand Down Expand Up @@ -50,6 +53,7 @@ export function createStartAgentDeviceRemoteSessionBuildFunction(
name: 'Start agent device remote session',
__metricsId: 'eas/start_agent_device_remote_session',
inputProviders: [
...createServeSimLaunchInputProviders(),
BuildStepInput.createProvider({
id: 'package_version',
required: false,
Expand All @@ -67,6 +71,7 @@ export function createStartAgentDeviceRemoteSessionBuildFunction(
// back to the API server), EAS_SIMULATOR_NGROK_TUNNEL_DOMAIN (base domain
// for our ngrok tunnels), and NGROK_AUTHTOKEN (to authenticate them).
const deviceRunSessionId = getDeviceRunSessionIdOrThrow(env);
const launch = parseServeSimLaunchInputs(inputs);
const ngrokTunnelDomain = getNgrokTunnelDomainOrThrow(env);
const ngrokAuthtoken = getNgrokAuthtokenOrThrow(env);

Expand Down Expand Up @@ -114,7 +119,14 @@ export function createStartAgentDeviceRemoteSessionBuildFunction(
env,
logger,
timeoutMs: STARTUP_TIMEOUT_MS,
launchAppIdentifier: launch.launchAppIdentifier,
launchArgs: launch.launchArgs,
openUrl: launch.openUrl,
});
const launchDescription = describeServeSimLaunch(launch);
if (launchDescription) {
logger.info(launchDescription);
}
logger.info(`Web preview URL: ${serveSim.previewUrl}`);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ import { sleepAsync } from '../../utils/retry';
import { pollArgentArtifactsForUploadAsync } from '../utils/argentArtifacts';
import { ARGENT_EVENT_LOG_FILENAME, startArgentEventCollectionAsync } from '../utils/argentEvents';
import {
createServeSimLaunchInputProviders,
describeServeSimLaunch,
ensureFfmpegInstalledAsync,
getDeviceRunSessionIdOrThrow,
getNgrokAuthtokenOrThrow,
getNgrokTunnelDomainOrThrow,
parseServeSimLaunchInputs,
selectXcodeDeveloperDirectoryAsync,
spawnDetached,
startNgrokTunnelAsync,
Expand Down Expand Up @@ -63,6 +66,7 @@ export function createStartArgentRemoteSessionBuildFunction(
name: 'Start argent remote session',
__metricsId: 'eas/start_argent_remote_session',
inputProviders: [
...createServeSimLaunchInputProviders(),
BuildStepInput.createProvider({
id: 'package_version',
required: false,
Expand All @@ -80,6 +84,7 @@ export function createStartArgentRemoteSessionBuildFunction(
// back to the API server), EAS_SIMULATOR_NGROK_TUNNEL_DOMAIN (base domain
// for our ngrok tunnels), and NGROK_AUTHTOKEN (to authenticate them).
const deviceRunSessionId = getDeviceRunSessionIdOrThrow(env);
const launch = parseServeSimLaunchInputs(inputs);
const ngrokTunnelDomain = getNgrokTunnelDomainOrThrow(env);
const ngrokAuthtoken = getNgrokAuthtokenOrThrow(env);

Expand Down Expand Up @@ -201,7 +206,14 @@ export function createStartArgentRemoteSessionBuildFunction(
env,
logger,
timeoutMs: STARTUP_TIMEOUT_MS,
launchAppIdentifier: launch.launchAppIdentifier,
launchArgs: launch.launchArgs,
openUrl: launch.openUrl,
});
const launchDescription = describeServeSimLaunch(launch);
if (launchDescription) {
logger.info(launchDescription);
}
webPreviewUrl = serveSim.previewUrl;
logger.info(`Web preview URL: ${webPreviewUrl}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { BuildFunction, BuildRuntimePlatform } from '@expo/steps';

import { CustomBuildContext } from '../../customBuildContext';
import {
createServeSimLaunchInputProviders,
describeServeSimLaunch,
getDeviceRunSessionIdOrThrow,
getNgrokTunnelDomainOrThrow,
parseServeSimLaunchInputs,
selectXcodeDeveloperDirectoryAsync,
startServeSimWithTunnelAsync,
uploadRemoteSessionConfigAsync,
Expand All @@ -21,11 +24,17 @@ export function createStartServeSimRemoteSessionBuildFunction(
name: 'Start serve-sim remote session',
__metricsId: 'eas/start_serve_sim_remote_session',
supportedRuntimePlatforms: [BuildRuntimePlatform.DARWIN],
fn: async ({ logger }, { env, signal }) => {
inputProviders: createServeSimLaunchInputProviders(),
fn: async ({ logger }, { inputs, env, signal }) => {
const deviceRunSessionId = getDeviceRunSessionIdOrThrow(env);
const ngrokTunnelDomain = getNgrokTunnelDomainOrThrow(env);
const launch = parseServeSimLaunchInputs(inputs);

logger.info('Starting serve-sim remote session.');
const launchDescription = describeServeSimLaunch(launch);
if (launchDescription) {
logger.info(launchDescription);
}

await selectXcodeDeveloperDirectoryAsync({ env, logger });

Expand All @@ -34,6 +43,9 @@ export function createStartServeSimRemoteSessionBuildFunction(
env,
logger,
timeoutMs: STARTUP_TIMEOUT_MS,
launchAppIdentifier: launch.launchAppIdentifier,
launchArgs: launch.launchArgs,
openUrl: launch.openUrl,
});
logger.info(`Preview URL: ${serveSim.previewUrl}`);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import { turtleFetch } from '../../../utils/turtleFetch';
import { sleepAsync } from '../../../utils/retry';
import {
createServeSimArgs,
describeServeSimLaunch,
ensureFfmpegInstalledAsync,
fetchServeSimTurnArgsAsync,
metricsCorsOriginToServeSimArgs,
parseServeSimLaunchInputs,
startNgrokTunnelAsync,
turnIceServersToServeSimArgs,
waitForDeviceRunSessionStoppedAsync,
Expand Down Expand Up @@ -108,6 +110,69 @@ function createEnvMock(): BuildStepEnv {
return { DEVICE_RUN_SESSION_ID: 'drs-id' } as unknown as BuildStepEnv;
}

describe(parseServeSimLaunchInputs, () => {
it('reads the launch identifier, arguments and URL', () => {
expect(
parseServeSimLaunchInputs({
launch_app_identifier: { value: 'host.exp.Exponent' },
launch_args: { value: ['-EXDevMenuIsOnboardingFinished', '1'] },
open_url: { value: 'exp://127.0.0.1:8081' },
})
).toEqual({
launchAppIdentifier: 'host.exp.Exponent',
launchArgs: ['-EXDevMenuIsOnboardingFinished', '1'],
openUrl: 'exp://127.0.0.1:8081',
});
});

it('defaults to no launch when the step declares nothing', () => {
expect(parseServeSimLaunchInputs({})).toEqual({
launchAppIdentifier: undefined,
launchArgs: [],
openUrl: undefined,
});
});

it('rejects launch arguments that are not a list of strings', () => {
expect(() =>
parseServeSimLaunchInputs({
launch_app_identifier: { value: 'host.exp.Exponent' },
launch_args: { value: 'oops' },
})
).toThrow('must be an array of strings');
});

it('rejects launch arguments with no application to launch', () => {
expect(() => parseServeSimLaunchInputs({ launch_args: { value: ['-flag'] } })).toThrow(
'Pass "launch_app_identifier"'
);
});

it('rejects a URL with no application to open it in', () => {
expect(() =>
parseServeSimLaunchInputs({ open_url: { value: 'exp://127.0.0.1:8081' } })
).toThrow('Pass "launch_app_identifier"');
});
});

describe(describeServeSimLaunch, () => {
it('says nothing when there is no application to launch', () => {
expect(describeServeSimLaunch({ launchArgs: [] })).toBeNull();
});

it('names the application, its arguments and the URL', () => {
expect(
describeServeSimLaunch({
launchAppIdentifier: 'host.exp.Exponent',
launchArgs: ['-flag', '1'],
openUrl: 'exp://127.0.0.1:8081',
})
).toBe(
'serve-sim will launch host.exp.Exponent with arguments ["-flag","1"], then open exp://127.0.0.1:8081.'
);
});
});

describe(createServeSimArgs, () => {
it('uses the latest Expo package and applies the EAS streaming policy', () => {
expect(
Expand Down Expand Up @@ -139,6 +204,31 @@ describe(createServeSimArgs, () => {
]);
});

it('appends the launch flags after the streaming policy', () => {
const args = createServeSimArgs({
port: 4321,
launchAppIdentifier: 'host.exp.Exponent',
launchArgs: ['-EXDevMenuIsOnboardingFinished', '1'],
openUrl: 'exp://127.0.0.1:8081',
});
expect(args.slice(-8)).toEqual([
'--launch-app-identifier',
'host.exp.Exponent',
'--launch-arg',
'-EXDevMenuIsOnboardingFinished',
'--launch-arg',
'1',
'--open-url',
'exp://127.0.0.1:8081',
]);
});

it('omits the launch flags when there is no application to launch', () => {
const args = createServeSimArgs({ port: 4321 });
expect(args.some(argument => argument.startsWith('--launch'))).toBe(false);
expect(args).not.toContain('--open-url');
});

it('appends metrics CORS args after the TURN args when provided', () => {
const args = createServeSimArgs({
port: 4321,
Expand Down
Loading