Skip to content
Open
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
14 changes: 7 additions & 7 deletions examples/test-app/replays/gesture-lab-android.ad
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,40 @@ wait "Gesture lab" 30000
wait "gesture canary ready" 5000
wait "two-pointer pan activations 0" 5000

gesture pan 350 1100 180 0 500
gesture pan 220 700 180 0 500
wait "two-pointer pan activations 0" 5000

gesture pan 350 1100 180 0 500 --pointer-count 2
gesture pan 220 700 180 0 500 --pointer-count 2
wait "two-pointer pan activations 1" 5000

gesture fling left 850 1100 300
gesture fling left 500 700 300
wait "fling 1" 5000

gesture fling right 750 1100 300
gesture fling left 500 700 300
wait "fling 2" 5000

open "${APP_TARGET}" --relaunch --launch-url "${APP_URL}"
react-native dismiss-overlay
wait "gesture canary ready" 5000
wait "pan changed no, pinch changed no, rotate changed no" 30000

gesture pinch 1.5 750 1100
gesture pinch 1.5 360 700
wait "pan changed no, pinch changed yes, rotate changed no" 5000

open "${APP_TARGET}" --relaunch --launch-url "${APP_URL}"
react-native dismiss-overlay
wait "gesture canary ready" 5000
wait "pan changed no, pinch changed no, rotate changed no" 30000

gesture rotate 35 750 1100
gesture rotate 35 360 700
wait "pan changed no, pinch changed no, rotate changed yes" 5000

open "${APP_TARGET}" --relaunch --launch-url "${APP_URL}"
react-native dismiss-overlay
wait "gesture canary ready" 5000
wait "pan changed no, pinch changed no, rotate changed no" 30000

gesture transform 750 1100 60 0 1.75 30 600
gesture transform 360 700 60 0 1.75 30 600
wait "pan changed yes, pinch changed yes, rotate changed yes" 5000

close
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
"detox"
],
"dependencies": {
"@limrun/api": "^0.24.5",
"yaml": "^2.9.0"
},
"devDependencies": {
Expand Down
82 changes: 82 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/layering/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const UNRANKED_ZONES: ReadonlySet<string> = new Set([
'compat',
'mcp',
'metro',
'providers',
'recording',
'remote',
'replay',
Expand Down
61 changes: 60 additions & 1 deletion src/__tests__/cli-react-devtools-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
hashRemoteConfigFile,
writeRemoteConnectionState,
} from '../remote/remote-connection-state.ts';
import type { DaemonResponse } from '../daemon/client/daemon-client.ts';
import type { DaemonRequest, DaemonResponse } from '../daemon/client/daemon-client.ts';

afterEach(() => {
vi.clearAllMocks();
Expand Down Expand Up @@ -73,3 +73,62 @@ test('react-devtools uses active remote connection session after defaults are me
assert.equal(vi.mocked(runReactDevtoolsCommand).mock.calls.length, 1);
assert.equal(vi.mocked(runReactDevtoolsCommand).mock.calls[0]?.[1]?.session, 'adc-android');
});

test('react-devtools starts Limrun port reverse through the daemon', async () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-device-react-devtools-limrun-'));
const stateDir = path.join(tempRoot, 'state');
const originalExit = process.exit;
let exitCode: number | undefined;
const restoreEnv = installIsolatedCliTestEnv();
(process as any).exit = ((code?: number) => {
exitCode = code ?? 0;
}) as typeof process.exit;
const remoteConfigPath = path.join(tempRoot, 'limrun.json');
fs.writeFileSync(remoteConfigPath, JSON.stringify({ platform: 'android' }));
writeRemoteConnectionState({
stateDir,
state: {
version: 1,
session: 'limrun-android',
remoteConfigPath,
remoteConfigHash: hashRemoteConfigFile(remoteConfigPath),
daemon: {},
tenant: 'limrun',
runId: 'run-1',
leaseId: 'lease-1',
leaseBackend: 'android-instance',
leaseProvider: 'limrun',
platform: 'android',
connectedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
});
const requests: Array<Omit<DaemonRequest, 'token'>> = [];
const sendToDaemon = async (request: Omit<DaemonRequest, 'token'>): Promise<DaemonResponse> => {
requests.push(request);
return { ok: true, data: {} };
};
vi.mocked(runReactDevtoolsCommand).mockImplementationOnce(async (_args, options) => {
await options?.configureDirectPortReverse?.();
return 0;
});

try {
await runCli(['react-devtools', 'start', '--state-dir', stateDir], { sendToDaemon });
} finally {
restoreEnv();
process.exit = originalExit;
fs.rmSync(tempRoot, { recursive: true, force: true });
}

assert.equal(exitCode, 0);
assert.equal(requests.length, 1);
const request = requests[0];
assert.equal(request?.command, 'runtime');
assert.deepEqual(request?.positionals, ['port-reverse']);
assert.equal(request?.session, 'limrun-android');
assert.equal(request?.flags?.leaseProvider, 'limrun');
assert.equal(request?.flags?.devicePort, 8097);
assert.equal(request?.flags?.hostPort, 8097);
assert.equal(request?.flags?.portReverseName, 'react-devtools');
});
51 changes: 51 additions & 0 deletions src/__tests__/cli-react-devtools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,57 @@ test('react-devtools stop cleans up remote companion', async () => {
});
});

test('react-devtools start configures direct remote port reverse', async () => {
const configureDirectPortReverse = vi.fn(async () => undefined);
vi.mocked(runCmdStreaming).mockResolvedValueOnce({
exitCode: 0,
stdout: '',
stderr: '',
});

const exitCode = await runReactDevtoolsCommand(['start'], {
stateDir: '/tmp/agent-device-state',
session: 'default',
flags: {
...remoteBridgeScope,
metroProxyBaseUrl: undefined,
leaseBackend: 'android-instance',
leaseProvider: 'limrun',
remoteConfig: '/tmp/remote.json',
session: 'default',
},
configureDirectPortReverse,
});

assert.equal(exitCode, 0);
assert.equal(configureDirectPortReverse.mock.calls.length, 1);
assertNoRemoteCompanion();
});

test('react-devtools start keeps bridge-backed Android on companion tunnel', async () => {
const env = { ...process.env };
const configureDirectPortReverse = vi.fn(async () => undefined);
mockRemoteCompanionSuccess();

const exitCode = await runReactDevtoolsCommand(['start'], {
stateDir: '/tmp/agent-device-state',
session: 'default',
cwd: '/tmp/project',
env,
flags: {
...remoteBridgeScope,
leaseBackend: 'android-instance',
remoteConfig: '/tmp/remote.json',
session: 'default',
},
configureDirectPortReverse,
});

assert.equal(exitCode, 0);
assert.equal(configureDirectPortReverse.mock.calls.length, 0);
assertRemoteCompanionStarted(env);
});

test('react-devtools skips companion for non-bridge remote sessions', async () => {
await runStatusWithoutCompanion({
...remoteBridgeScope,
Expand Down
Loading
Loading