Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
@@ -0,0 +1,109 @@
import child_process from 'child_process';
import fs from 'fs';
import path from 'path';
import {IOSProjectInfo} from '@react-native-community/cli-types';
import {Device} from '../../../types';
import {runOnSimulator} from '../runOnSimulator';
import {buildProject} from '../../buildCommand/buildProject';
import installApp from '../installApp';
import {FlagsT} from '../createRun';

jest.mock('child_process');
jest.mock('fs', () => ({existsSync: jest.fn()}));
jest.mock('../../buildCommand/buildProject');
jest.mock('../installApp');

const xcodeProject: IOSProjectInfo = {
name: 'TestApp.xcworkspace',
path: '/path/to/TestApp.xcworkspace',
isWorkspace: true,
};

const simulator: Device = {
name: 'iPhone 15',
udid: 'AAAA-BBBB-CCCC',
state: 'Booted',
type: 'simulator',
};

const args = {} as FlagsT;
const developerDir = '/Applications/Xcode.app/Contents/Developer';

beforeEach(() => {
jest.clearAllMocks();
(buildProject as jest.Mock).mockResolvedValue('');
(installApp as jest.Mock).mockResolvedValue(undefined);
(child_process.execFileSync as jest.Mock).mockReturnValue(
`${developerDir}\n`,
);
});

test('opens Simulator.app with the device UDID when it exists', async () => {
(fs.existsSync as jest.Mock).mockImplementation((target) =>
String(target).endsWith('Simulator.app'),
);

await runOnSimulator(
xcodeProject,
'ios',
'Debug',
'TestApp',
args,
simulator,
);

expect(child_process.execFileSync).toHaveBeenCalledWith('open', [
`${developerDir}/Applications/Simulator.app`,
'--args',
'-CurrentDeviceUDID',
simulator.udid,
]);
});

test('falls back to DeviceHub.app without the UDID when Simulator.app is absent', async () => {
(fs.existsSync as jest.Mock).mockImplementation((target) =>
String(target).endsWith('DeviceHub.app'),
);

await runOnSimulator(
xcodeProject,
'ios',
'Debug',
'TestApp',
args,
simulator,
);

const deviceHubPath = path.join(
developerDir,
'..',
'Applications',
'DeviceHub.app',
);
expect(child_process.execFileSync).toHaveBeenCalledWith('open', [
deviceHubPath,
]);
// DeviceHub cannot focus a specific device, so the UDID must not be passed.
expect(child_process.execFileSync).not.toHaveBeenCalledWith(
'open',
expect.arrayContaining(['-CurrentDeviceUDID']),
);
});

test('does not boot the simulator when it is already booted', async () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);

await runOnSimulator(
xcodeProject,
'ios',
'Debug',
'TestApp',
args,
simulator,
);

expect(child_process.spawnSync).not.toHaveBeenCalledWith(
'xcrun',
expect.arrayContaining(['boot']),
);
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import child_process from 'child_process';
import fs from 'fs';
import path from 'path';
import {IOSProjectInfo} from '@react-native-community/cli-types';
import {logger} from '@react-native-community/cli-tools';
import {ApplePlatform, Device} from '../../types';
Expand Down Expand Up @@ -32,12 +34,34 @@ export async function runOnSimulator(
.execFileSync('xcode-select', ['-p'], {encoding: 'utf8'})
.trim();

child_process.execFileSync('open', [
`${activeDeveloperDir}/Applications/Simulator.app`,
'--args',
'-CurrentDeviceUDID',
simulator.udid,
]);
// Xcode 27 replaces Simulator.app with DeviceHub.app and relocates it from
// <Xcode>/Contents/Developer/Applications to <Xcode>/Contents/Applications.
// Prefer Simulator.app while it exists (stable Xcode); fall back to DeviceHub
// for Xcode 27+. See https://developer.apple.com/documentation/xcode/device-hub
const simulatorApp = path.join(
activeDeveloperDir,
'Applications',
'Simulator.app',
);
const deviceHubApp = path.join(
activeDeveloperDir,
'..',
'Applications',
'DeviceHub.app',
);

if (fs.existsSync(simulatorApp)) {
child_process.execFileSync('open', [
simulatorApp,
'--args',
'-CurrentDeviceUDID',
simulator.udid,
]);
} else if (fs.existsSync(deviceHubApp)) {
// DeviceHub gives us no way to focus a specific device, so we open it
// without the -CurrentDeviceUDID argument.
child_process.execFileSync('open', [deviceHubApp]);
Comment thread
huntie marked this conversation as resolved.
Outdated
}

if (simulator.state !== 'Booted') {
bootSimulator(simulator);
Expand Down
Loading