Skip to content

Commit d0e771d

Browse files
committed
fixes
1 parent fa3fc09 commit d0e771d

3 files changed

Lines changed: 129 additions & 8 deletions

File tree

devbox.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@
8787
"reset-ios": [
8888
"xcrun simctl shutdown all || true",
8989
"xcrun simctl erase all || true",
90-
"rm -rf ~/Library/Developer/CoreSimulator/Devices",
91-
"echo \"Simulators reset. Recreate via devbox run start-ios.\"",
90+
"xcrun simctl delete all || true",
91+
"xcrun simctl delete unavailable || true",
92+
"killall -9 com.apple.CoreSimulatorService 2>/dev/null || true",
93+
"echo \"Simulators reset via simctl. Recreate via devbox run start-ios.\"",
9294
],
9395
"stop-android": [
9496
"if command -v adb >/dev/null 2>&1; then",

examples/E2E/.detoxrc.js

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,81 @@
1+
const { execSync } = require('child_process');
2+
3+
const defaultIOSDeviceCandidates = (() => {
4+
const fromEnv = (process.env.IOS_DEVICE_NAMES || 'iPhone 14')
5+
.split(',')
6+
.map((name) => name.trim())
7+
.filter(Boolean);
8+
return Array.from(new Set(['iPhone 17', ...fromEnv, 'iPhone 14']));
9+
})();
10+
11+
const safeParseJSON = (cmd) => {
12+
try {
13+
return JSON.parse(
14+
execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] }).toString()
15+
);
16+
} catch (_) {
17+
return null;
18+
}
19+
};
20+
21+
const listAvailableDevices = () => {
22+
const parsed = safeParseJSON('xcrun simctl list devices -j');
23+
if (!parsed || !parsed.devices) return [];
24+
const devices = [];
25+
Object.values(parsed.devices).forEach((group) => {
26+
(group || []).forEach((device) => {
27+
if (device.isAvailable || device.availability === '(available)') {
28+
devices.push(device);
29+
}
30+
});
31+
});
32+
return devices;
33+
};
34+
35+
const listAvailableDeviceTypes = () => {
36+
const parsed = safeParseJSON('xcrun simctl list devicetypes -j');
37+
if (!parsed || !parsed.devicetypes) return new Set();
38+
return new Set(parsed.devicetypes.map((dt) => dt.name.toLowerCase()));
39+
};
40+
41+
const baseName = (name) => name.split(' (')[0].trim().toLowerCase();
42+
43+
const detectIOSDevice = () => {
44+
if (process.env.DETOX_IOS_DEVICE) {
45+
return { type: process.env.DETOX_IOS_DEVICE };
46+
}
47+
48+
const devices = listAvailableDevices();
49+
const preferredDevice = defaultIOSDeviceCandidates.find((candidate) =>
50+
devices.some((d) => baseName(d.name) === candidate.toLowerCase())
51+
);
52+
if (preferredDevice) {
53+
const found = devices.find(
54+
(d) => baseName(d.name) === preferredDevice.toLowerCase()
55+
);
56+
if (found) return { name: found.name };
57+
}
58+
const anyIphoneDevice = devices.find((d) =>
59+
d.name.toLowerCase().includes('iphone')
60+
);
61+
if (anyIphoneDevice) return { name: anyIphoneDevice.name };
62+
63+
const availableTypes = listAvailableDeviceTypes();
64+
const preferredType = defaultIOSDeviceCandidates.find((candidate) =>
65+
availableTypes.has(candidate.toLowerCase())
66+
);
67+
if (preferredType) return { type: preferredType };
68+
69+
const anyIphoneType = Array.from(availableTypes).find((t) =>
70+
t.includes('iphone')
71+
);
72+
if (anyIphoneType) return { type: anyIphoneType };
73+
74+
return { type: defaultIOSDeviceCandidates[0] };
75+
};
76+
77+
const iosDevice = detectIOSDevice();
78+
179
/** @type {Detox.DetoxConfig} */
280
module.exports = {
381
testRunner: {
@@ -51,8 +129,8 @@ module.exports = {
51129
simulator: {
52130
type: 'ios.simulator',
53131
device: {
54-
// Allow CI/local override; defaults to iPhone 17 on latest runtime.
55-
type: process.env.DETOX_IOS_DEVICE || 'iPhone 17'
132+
// Allow CI/local override; defaults to an available simulator by name (or type fallback).
133+
...iosDevice
56134
}
57135
},
58136
attached: {

scripts/ios-setup.sh

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,31 @@ EOF
5656

5757
ensure_simctl
5858

59+
ensure_core_sim_service() {
60+
local output status
61+
output="$(xcrun simctl list devices -j 2>&1)" || status=$?
62+
if [[ -n "${status:-}" ]]; then
63+
echo "simctl failed while listing devices (status ${status}). CoreSimulatorService may be unhealthy." >&2
64+
echo "Try restarting it:" >&2
65+
echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2
66+
echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2
67+
echo "Then open Simulator once and rerun devbox run setup-ios." >&2
68+
echo "simctl error output:" >&2
69+
echo "$output" >&2
70+
return 1
71+
fi
72+
73+
if echo "$output" | grep -q "CoreSimulatorService connection became invalid"; then
74+
echo "CoreSimulatorService is not healthy. Try restarting it:" >&2
75+
echo " killall -9 com.apple.CoreSimulatorService 2>/dev/null || true" >&2
76+
echo " launchctl kickstart -k gui/$UID/com.apple.CoreSimulatorService" >&2
77+
echo "Then open Simulator once and rerun devbox run setup-ios." >&2
78+
echo "simctl error output:" >&2
79+
echo "$output" >&2
80+
return 1
81+
fi
82+
}
83+
5984
pick_runtime() {
6085
local preferred="$1"
6186
local json choice
@@ -95,6 +120,13 @@ existing_device_udid_any_runtime() {
95120
xcrun simctl list devices -j | jq -r --arg name "$name" '.devices[]?[]? | select(.name == $name) | .udid' | head -n1
96121
}
97122

123+
device_data_dir_exists() {
124+
local udid="${1:-}"
125+
[[ -n "$udid" ]] || return 1
126+
local dir="$HOME/Library/Developer/CoreSimulator/Devices/$udid"
127+
[[ -d "$dir" ]]
128+
}
129+
98130
devicetype_id_for_name() {
99131
local name="$1"
100132
xcrun simctl list devicetypes -j | jq -r --arg name "$name" '.devicetypes[] | select((.name|ascii_downcase) == ($name|ascii_downcase)) | .identifier' | head -n1
@@ -105,8 +137,12 @@ ensure_device() {
105137

106138
# If a device with this name already exists anywhere, reuse it.
107139
if existing_udid=$(existing_device_udid_any_runtime "$base_name"); [[ -n "${existing_udid}" ]]; then
108-
echo "Found existing ${base_name}: ${existing_udid}"
109-
return 0
140+
if device_data_dir_exists "$existing_udid"; then
141+
echo "Found existing ${base_name}: ${existing_udid}"
142+
return 0
143+
fi
144+
echo "Existing ${base_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..."
145+
xcrun simctl delete "$existing_udid" || true
110146
fi
111147

112148
local choice runtime_id runtime_name
@@ -126,8 +162,12 @@ ensure_device() {
126162

127163
# Also check for an existing device with the runtime-qualified display name.
128164
if existing_udid=$(existing_device_udid_any_runtime "$display_name"); [[ -n "${existing_udid}" ]]; then
129-
echo "Found existing ${display_name}: ${existing_udid}"
130-
return 0
165+
if device_data_dir_exists "$existing_udid"; then
166+
echo "Found existing ${display_name}: ${existing_udid}"
167+
return 0
168+
fi
169+
echo "Existing ${display_name} (${existing_udid}) is missing its data directory. Deleting stale simulator..."
170+
xcrun simctl delete "$existing_udid" || true
131171
fi
132172

133173
echo "Creating ${display_name}..."
@@ -136,6 +176,7 @@ ensure_device() {
136176
}
137177

138178
main() {
179+
ensure_core_sim_service || return 1
139180
IFS=',' read -r -a devices <<<"${IOS_DEVICE_NAMES:-iPhone 14,iPhone 17}"
140181
local runtime="${IOS_RUNTIME:-18.5}"
141182
for device in "${devices[@]}"; do

0 commit comments

Comments
 (0)