Skip to content

Commit ad28a8a

Browse files
committed
Add iOS UI tests on simulator
1 parent 4ef0dae commit ad28a8a

6 files changed

Lines changed: 420 additions & 3 deletions

File tree

.github/workflows/ios-ui-tests.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: iOS UI Tests
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
paths:
7+
- ".github/workflows/ios-ui-tests.yml"
8+
- ".github/workflows/ios-simulator-smoke.yml"
9+
- "iosApp/**"
10+
- "shared/**"
11+
- "settings.gradle.kts"
12+
- "build.gradle.kts"
13+
- "gradle/**"
14+
- "gradlew"
15+
- "gradlew.bat"
16+
17+
jobs:
18+
ui-tests:
19+
runs-on: macos-15
20+
timeout-minutes: 40
21+
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v5
25+
26+
- name: Set up Java 17
27+
uses: actions/setup-java@v5
28+
with:
29+
distribution: temurin
30+
java-version: "17"
31+
32+
- name: Ensure Gradle wrapper is executable
33+
run: chmod +x ./gradlew
34+
35+
- name: Select available iPhone simulator
36+
id: simulator
37+
shell: bash
38+
run: |
39+
set -euo pipefail
40+
python3 - <<'PY' >> "$GITHUB_OUTPUT"
41+
import json
42+
import subprocess
43+
44+
data = json.loads(
45+
subprocess.check_output(
46+
["xcrun", "simctl", "list", "devices", "available", "-j"],
47+
text=True,
48+
)
49+
)
50+
51+
selected = None
52+
for runtime in sorted(data["devices"].keys(), reverse=True):
53+
if "iOS" not in runtime:
54+
continue
55+
iphones = [
56+
device
57+
for device in data["devices"][runtime]
58+
if device.get("isAvailable") and device["name"].startswith("iPhone")
59+
]
60+
if iphones:
61+
selected = (runtime, iphones[0]["name"], iphones[0]["udid"])
62+
break
63+
64+
if selected is None:
65+
raise SystemExit("No available iPhone simulator found")
66+
67+
runtime, name, udid = selected
68+
print(f"runtime={runtime}")
69+
print(f"name={name}")
70+
print(f"udid={udid}")
71+
PY
72+
73+
- name: Boot simulator
74+
shell: bash
75+
run: |
76+
set -euo pipefail
77+
xcrun simctl boot "${{ steps.simulator.outputs.udid }}" || true
78+
xcrun simctl bootstatus "${{ steps.simulator.outputs.udid }}" -b
79+
80+
- name: Run XCUITests
81+
shell: bash
82+
run: |
83+
set -euo pipefail
84+
mkdir -p build/ios-ui-tests
85+
xcodebuild \
86+
-project iosApp/iosApp.xcodeproj \
87+
-scheme iosAppUITests \
88+
-configuration Debug \
89+
-sdk iphonesimulator \
90+
-derivedDataPath build/ios-ui-tests/derived-data \
91+
-resultBundlePath build/ios-ui-tests/ios-ui-tests.xcresult \
92+
-destination "id=${{ steps.simulator.outputs.udid }}" \
93+
CODE_SIGNING_ALLOWED=NO \
94+
CODE_SIGNING_REQUIRED=NO \
95+
CODE_SIGN_IDENTITY="" \
96+
DEVELOPMENT_TEAM="" \
97+
test | tee build/ios-ui-tests/xcodebuild-ui-tests.log
98+
99+
- name: Capture post-test screenshot
100+
if: always()
101+
shell: bash
102+
run: |
103+
set -euo pipefail
104+
mkdir -p build/ios-ui-tests
105+
xcrun simctl io "${{ steps.simulator.outputs.udid }}" screenshot build/ios-ui-tests/post-test-screenshot.png || true
106+
107+
- name: Upload UI test artifacts
108+
if: always()
109+
uses: actions/upload-artifact@v6
110+
with:
111+
name: ios-ui-tests
112+
if-no-files-found: ignore
113+
path: |
114+
build/ios-ui-tests/xcodebuild-ui-tests.log
115+
build/ios-ui-tests/ios-ui-tests.xcresult
116+
build/ios-ui-tests/post-test-screenshot.png

iosApp/iosApp.xcodeproj/project.pbxproj

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,39 @@
1111
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
1212
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
1313
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
14+
A11F10012700000000000001 /* iosAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11F10042700000000000001 /* iosAppUITests.swift */; };
1415
/* End PBXBuildFile section */
1516

17+
/* Begin PBXContainerItemProxy section */
18+
A11F10032700000000000001 /* PBXContainerItemProxy */ = {
19+
isa = PBXContainerItemProxy;
20+
containerPortal = 7555FF73242A565900829871 /* Project object */;
21+
proxyType = 1;
22+
remoteGlobalIDString = 7555FF7A242A565900829871;
23+
remoteInfo = iosApp;
24+
};
25+
/* End PBXContainerItemProxy section */
26+
1627
/* Begin PBXFileReference section */
1728
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1829
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
1930
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
2031
7555FF7B242A565900829871 /* ScoutEventi.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ScoutEventi.app"; sourceTree = BUILT_PRODUCTS_DIR; };
2132
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
2233
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
34+
A11F10042700000000000001 /* iosAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosAppUITests.swift; sourceTree = "<group>"; };
35+
A11F10052700000000000001 /* iosAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iosAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
2336
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
2437
/* End PBXFileReference section */
2538

2639
/* Begin PBXFrameworksBuildPhase section */
40+
A11F10092700000000000001 /* Frameworks */ = {
41+
isa = PBXFrameworksBuildPhase;
42+
buildActionMask = 2147483647;
43+
files = (
44+
);
45+
runOnlyForDeploymentPostprocessing = 0;
46+
};
2747
F85CB1118929364A9C6EFABC /* Frameworks */ = {
2848
isa = PBXFrameworksBuildPhase;
2949
buildActionMask = 2147483647;
@@ -54,6 +74,7 @@
5474
children = (
5575
AB1DB47929225F7C00F7AF9C /* Configuration */,
5676
7555FF7D242A565900829871 /* iosApp */,
77+
A11F10072700000000000001 /* iosAppUITests */,
5778
7555FF7C242A565900829871 /* Products */,
5879
42799AB246E5F90AF97AA0EF /* Frameworks */,
5980
);
@@ -62,7 +83,8 @@
6283
7555FF7C242A565900829871 /* Products */ = {
6384
isa = PBXGroup;
6485
children = (
65-
7555FF7B242A565900829871 /* My application.app */,
86+
7555FF7B242A565900829871 /* ScoutEventi.app */,
87+
A11F10052700000000000001 /* iosAppUITests.xctest */,
6688
);
6789
name = Products;
6890
sourceTree = "<group>";
@@ -79,6 +101,14 @@
79101
path = iosApp;
80102
sourceTree = "<group>";
81103
};
104+
A11F10072700000000000001 /* iosAppUITests */ = {
105+
isa = PBXGroup;
106+
children = (
107+
A11F10042700000000000001 /* iosAppUITests.swift */,
108+
);
109+
path = iosAppUITests;
110+
sourceTree = "<group>";
111+
};
82112
AB1DB47929225F7C00F7AF9C /* Configuration */ = {
83113
isa = PBXGroup;
84114
children = (
@@ -90,6 +120,24 @@
90120
/* End PBXGroup section */
91121

92122
/* Begin PBXNativeTarget section */
123+
A11F10082700000000000001 /* iosAppUITests */ = {
124+
isa = PBXNativeTarget;
125+
buildConfigurationList = A11F100F2700000000000001 /* Build configuration list for PBXNativeTarget "iosAppUITests" */;
126+
buildPhases = (
127+
A11F10092700000000000001 /* Frameworks */,
128+
A11F100B2700000000000001 /* Sources */,
129+
A11F100A2700000000000001 /* Resources */,
130+
);
131+
buildRules = (
132+
);
133+
dependencies = (
134+
A11F100C2700000000000001 /* PBXTargetDependency */,
135+
);
136+
name = iosAppUITests;
137+
productName = iosAppUITests;
138+
productReference = A11F10052700000000000001 /* iosAppUITests.xctest */;
139+
productType = "com.apple.product-type.bundle.ui-testing";
140+
};
93141
7555FF7A242A565900829871 /* iosApp */ = {
94142
isa = PBXNativeTarget;
95143
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
@@ -117,6 +165,10 @@
117165
LastUpgradeCheck = 1130;
118166
ORGANIZATIONNAME = orgName;
119167
TargetAttributes = {
168+
A11F10082700000000000001 = {
169+
CreatedOnToolsVersion = 16.3;
170+
TestTargetID = 7555FF7A242A565900829871;
171+
};
120172
7555FF7A242A565900829871 = {
121173
CreatedOnToolsVersion = 11.3.1;
122174
};
@@ -135,12 +187,20 @@
135187
projectDirPath = "";
136188
projectRoot = "";
137189
targets = (
190+
A11F10082700000000000001 /* iosAppUITests */,
138191
7555FF7A242A565900829871 /* iosApp */,
139192
);
140193
};
141194
/* End PBXProject section */
142195

143196
/* Begin PBXResourcesBuildPhase section */
197+
A11F100A2700000000000001 /* Resources */ = {
198+
isa = PBXResourcesBuildPhase;
199+
buildActionMask = 2147483647;
200+
files = (
201+
);
202+
runOnlyForDeploymentPostprocessing = 0;
203+
};
144204
7555FF79242A565900829871 /* Resources */ = {
145205
isa = PBXResourcesBuildPhase;
146206
buildActionMask = 2147483647;
@@ -153,6 +213,14 @@
153213
/* End PBXResourcesBuildPhase section */
154214

155215
/* Begin PBXSourcesBuildPhase section */
216+
A11F100B2700000000000001 /* Sources */ = {
217+
isa = PBXSourcesBuildPhase;
218+
buildActionMask = 2147483647;
219+
files = (
220+
A11F10012700000000000001 /* iosAppUITests.swift in Sources */,
221+
);
222+
runOnlyForDeploymentPostprocessing = 0;
223+
};
156224
7555FF77242A565900829871 /* Sources */ = {
157225
isa = PBXSourcesBuildPhase;
158226
buildActionMask = 2147483647;
@@ -164,7 +232,47 @@
164232
};
165233
/* End PBXSourcesBuildPhase section */
166234

235+
/* Begin PBXTargetDependency section */
236+
A11F100C2700000000000001 /* PBXTargetDependency */ = {
237+
isa = PBXTargetDependency;
238+
target = 7555FF7A242A565900829871 /* iosApp */;
239+
targetProxy = A11F10032700000000000001 /* PBXContainerItemProxy */;
240+
};
241+
/* End PBXTargetDependency section */
242+
167243
/* Begin XCBuildConfiguration section */
244+
A11F100D2700000000000001 /* Debug */ = {
245+
isa = XCBuildConfiguration;
246+
buildSettings = {
247+
CODE_SIGN_IDENTITY = "Apple Development";
248+
CODE_SIGN_STYLE = Automatic;
249+
DEVELOPMENT_TEAM = "${TEAM_ID}";
250+
GENERATE_INFOPLIST_FILE = YES;
251+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
252+
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}.uitests";
253+
PRODUCT_NAME = "$(TARGET_NAME)";
254+
SWIFT_VERSION = 5.0;
255+
TARGETED_DEVICE_FAMILY = "1,2";
256+
TEST_TARGET_NAME = iosApp;
257+
};
258+
name = Debug;
259+
};
260+
A11F100E2700000000000001 /* Release */ = {
261+
isa = XCBuildConfiguration;
262+
buildSettings = {
263+
CODE_SIGN_IDENTITY = "Apple Development";
264+
CODE_SIGN_STYLE = Automatic;
265+
DEVELOPMENT_TEAM = "${TEAM_ID}";
266+
GENERATE_INFOPLIST_FILE = YES;
267+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
268+
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}.uitests";
269+
PRODUCT_NAME = "$(TARGET_NAME)";
270+
SWIFT_VERSION = 5.0;
271+
TARGETED_DEVICE_FAMILY = "1,2";
272+
TEST_TARGET_NAME = iosApp;
273+
};
274+
name = Release;
275+
};
168276
7555FFA3242A565B00829871 /* Debug */ = {
169277
isa = XCBuildConfiguration;
170278
baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
@@ -341,6 +449,15 @@
341449
defaultConfigurationIsVisible = 0;
342450
defaultConfigurationName = Release;
343451
};
452+
A11F100F2700000000000001 /* Build configuration list for PBXNativeTarget "iosAppUITests" */ = {
453+
isa = XCConfigurationList;
454+
buildConfigurations = (
455+
A11F100D2700000000000001 /* Debug */,
456+
A11F100E2700000000000001 /* Release */,
457+
);
458+
defaultConfigurationIsVisible = 0;
459+
defaultConfigurationName = Release;
460+
};
344461
7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
345462
isa = XCConfigurationList;
346463
buildConfigurations = (

0 commit comments

Comments
 (0)