commit 3260eac5182505530cdfebba420c572da70c6071
parent da94b8b712a6b8494c65c16681b3b421be04f327
Author: Michael Camilleri <[email protected]>
Date: Sat, 14 Mar 2026 18:02:31 +0900
Add UI tests for iOS
This adds UI tests for the iOS version of Listless. The tests are only
run on a simulated iPhone because XCUITest (amazingly) does not properly
support testing menu bar-related functionality on the iPad. If at some
future point, Apple adds this support, a separate UI test suite should
be added that will test iPad-specific code (primarily regarding keyboard
shortcuts).
Co-Authored-By: Claude 4.6 Opus <[email protected]>
Diffstat:
8 files changed, 316 insertions(+), 5 deletions(-)
diff --git a/Listless.xcodeproj/project.pbxproj b/Listless.xcodeproj/project.pbxproj
@@ -21,6 +21,7 @@
19699EC4FF57EF0D636B65E3 /* KeyValueSyncBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAE264D30C7692457B92E518 /* KeyValueSyncBridge.swift */; };
1A66A0454558B207AF9265D4 /* UndoToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658295C1386BFF48CE3C2419 /* UndoToast.swift */; };
1AA328A921EF8A7FDD03119A /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75B048B19C5219862BBED2E7 /* TestHelpers.swift */; };
+ 239F975836FD432A5FF04036 /* ListlessiOSUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88C0D6F2667BD14F29CB84E5 /* ListlessiOSUITests.swift */; };
264BD64C1DD30376E8BDAF79 /* KeyValueSyncBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAE264D30C7692457B92E518 /* KeyValueSyncBridge.swift */; };
269B93D5543770B464DFB37A /* TaskStoreOrderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3A2DDCE24E54ABCCFBBD4C /* TaskStoreOrderingTests.swift */; };
26F609518DE1055DF09B0159 /* TaskListView+NavigationHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712ADAD0FE66175DAA9A6D50 /* TaskListView+NavigationHeader.swift */; };
@@ -131,6 +132,13 @@
remoteGlobalIDString = 0FB4F07A37999BBC6DFE4DBB;
remoteInfo = "Listless macOS";
};
+ EE50B14FCB0F89292F1E2A01 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 3256C2BF8F1DAF371DA32120 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 34A03D42B91730DEAC2EBD8E;
+ remoteInfo = "Listless iOS";
+ };
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -150,6 +158,7 @@
/* Begin PBXFileReference section */
01E141436176F83594E2F26B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
067D666DF0AF1C53404ECF7C /* TaskRowSwipeGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskRowSwipeGesture.swift; sourceTree = "<group>"; };
+ 0B750F1634E250256AF3FEB6 /* Listless iOS UI Tests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = "Listless iOS UI Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
126108860D7878DDC3BECC4B /* Listless iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = "Listless iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
17DD7EDA74DAAFA27C84CA08 /* AccentColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentColor.swift; sourceTree = "<group>"; };
199CC2F58DD7CBA3F2229366 /* TaskRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskRowView.swift; sourceTree = "<group>"; };
@@ -184,6 +193,7 @@
75B048B19C5219862BBED2E7 /* TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = "<group>"; };
7A7BD42B1E3C71333FA24893 /* TaskListView+SyncUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+SyncUI.swift"; sourceTree = "<group>"; };
7C73E9D4C42CCABBF0F33543 /* Listless.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Listless.entitlements; sourceTree = "<group>"; };
+ 88C0D6F2667BD14F29CB84E5 /* ListlessiOSUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListlessiOSUITests.swift; sourceTree = "<group>"; };
8AACB5E40BEBA75E7FE8B930 /* TaskListView+Undo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TaskListView+Undo.swift"; sourceTree = "<group>"; };
9262207DAC21619BD9EDEE15 /* Listless.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Listless.entitlements; sourceTree = "<group>"; };
92D1AF0DE30657AAD86482CA /* TaskRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskRowView.swift; sourceTree = "<group>"; };
@@ -251,6 +261,7 @@
20349AB212EAB4FB5F21D959 /* UI */ = {
isa = PBXGroup;
children = (
+ 88C0D6F2667BD14F29CB84E5 /* ListlessiOSUITests.swift */,
EADAA53B8BCBC80AEFF191EF /* ListlessMacUITests.swift */,
);
name = UI;
@@ -271,6 +282,7 @@
3936BDEE64D16E6C4C85B3DD /* Products */ = {
isa = PBXGroup;
children = (
+ 0B750F1634E250256AF3FEB6 /* Listless iOS UI Tests.xctest */,
C71466C5CD1A5BA984352F8D /* Listless iOS Unit Tests.xctest */,
126108860D7878DDC3BECC4B /* Listless iOS.app */,
CCB5F87A520B1CD47F2F71D0 /* Listless macOS UI Tests.xctest */,
@@ -575,6 +587,24 @@
productReference = C71466C5CD1A5BA984352F8D /* Listless iOS Unit Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
+ E550C54CD9C9DD7CAF62B601 /* Listless iOS UI Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 54D8A976635B970879C3A083 /* Build configuration list for PBXNativeTarget "Listless iOS UI Tests" */;
+ buildPhases = (
+ 8A2F25417981C1E116479B25 /* Sources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 0338860A3D6FCAAFEECA5254 /* PBXTargetDependency */,
+ );
+ name = "Listless iOS UI Tests";
+ packageProductDependencies = (
+ );
+ productName = "Listless iOS UI Tests";
+ productReference = 0B750F1634E250256AF3FEB6 /* Listless iOS UI Tests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
ECF4D3D0597D0648A1FBC4A4 /* Listless macOS UI Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = CD8CF62F04D9EA70CFD04DDD /* Build configuration list for PBXNativeTarget "Listless macOS UI Tests" */;
@@ -622,6 +652,11 @@
DevelopmentTeam = 7TD7PZBNXP;
ProvisioningStyle = Automatic;
};
+ E550C54CD9C9DD7CAF62B601 = {
+ DevelopmentTeam = 7TD7PZBNXP;
+ ProvisioningStyle = Automatic;
+ TestTargetID = 34A03D42B91730DEAC2EBD8E;
+ };
ECF4D3D0597D0648A1FBC4A4 = {
DevelopmentTeam = 7TD7PZBNXP;
ProvisioningStyle = Automatic;
@@ -644,6 +679,7 @@
projectRoot = "";
targets = (
34A03D42B91730DEAC2EBD8E /* Listless iOS */,
+ E550C54CD9C9DD7CAF62B601 /* Listless iOS UI Tests */,
D533FDEDCE6DFCA2E8CB70F5 /* Listless iOS Unit Tests */,
0FB4F07A37999BBC6DFE4DBB /* Listless macOS */,
ECF4D3D0597D0648A1FBC4A4 /* Listless macOS UI Tests */,
@@ -799,6 +835,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 8A2F25417981C1E116479B25 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 239F975836FD432A5FF04036 /* ListlessiOSUITests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
D959A967B1BB3E9246C006D7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -815,6 +859,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 0338860A3D6FCAAFEECA5254 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 34A03D42B91730DEAC2EBD8E /* Listless iOS */;
+ targetProxy = EE50B14FCB0F89292F1E2A01 /* PBXContainerItemProxy */;
+ };
03DCFDF6E769DBF0DBC470F6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 34A03D42B91730DEAC2EBD8E /* Listless iOS */;
@@ -907,6 +956,24 @@
};
name = Release;
};
+ A59B52CB6CD91C01F164C0F6 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ GENERATE_INFOPLIST_FILE = YES;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = net.inqk.listless.ios.uitests;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = "Listless iOS";
+ };
+ name = Debug;
+ };
A5A377EA0FE470803E2B6BA1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1023,6 +1090,24 @@
};
name = Debug;
};
+ C8A7D13CC2AAF9DEA65CA25E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ GENERATE_INFOPLIST_FILE = YES;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = net.inqk.listless.ios.uitests;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = "Listless iOS";
+ };
+ name = Release;
+ };
CF2E8B9803D045F7F301E05E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1206,6 +1291,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
+ 54D8A976635B970879C3A083 /* Build configuration list for PBXNativeTarget "Listless iOS UI Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ A59B52CB6CD91C01F164C0F6 /* Debug */,
+ C8A7D13CC2AAF9DEA65CA25E /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Debug;
+ };
A549D68557611D588CB834B2 /* Build configuration list for PBXNativeTarget "Listless watchOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/Listless.xcodeproj/xcshareddata/xcschemes/Listless iOS.xcscheme b/Listless.xcodeproj/xcshareddata/xcschemes/Listless iOS.xcscheme
@@ -69,6 +69,18 @@
ReferencedContainer = "container:Listless.xcodeproj">
</BuildableReference>
</TestableReference>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES"
+ testExecutionOrdering = "random">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "E550C54CD9C9DD7CAF62B601"
+ BuildableName = "Listless iOS UI Tests.xctest"
+ BlueprintName = "Listless iOS UI Tests"
+ ReferencedContainer = "container:Listless.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
diff --git a/ListlessiOS/Helpers/TappableTextField.swift b/ListlessiOS/Helpers/TappableTextField.swift
@@ -11,9 +11,11 @@ struct TappableTextField: UIViewRepresentable {
let onEditingChanged: (Bool, _ shouldCreateNewTask: Bool) -> Void
var returnKeyType: UIReturnKeyType = .done
var onContentChange: ((String) -> Void)? = nil
+ var uiAccessibilityIdentifier: String? = nil
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
+ textView.accessibilityIdentifier = uiAccessibilityIdentifier
textView.delegate = context.coordinator
textView.font = TaskRowMetrics.bodyUIK
textView.backgroundColor = .clear
@@ -54,6 +56,7 @@ struct TappableTextField: UIViewRepresentable {
textView.returnKeyType = returnKeyType
textView.reloadInputViews()
}
+ textView.accessibilityIdentifier = uiAccessibilityIdentifier
textView.isEditable = !isCompleted && !isDragging
textView.isSelectable = !isCompleted && !isDragging
if let placeholder = textView.viewWithTag(100) as? UILabel {
@@ -65,7 +68,8 @@ struct TappableTextField: UIViewRepresentable {
let proposedWidth = proposal.width ?? uiView.bounds.width
let width = proposedWidth > 0 ? proposedWidth : (uiView.window?.bounds.width ?? 0)
guard width > 0 else { return nil }
- return uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
+ let fitted = uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
+ return CGSize(width: width, height: fitted.height)
}
func makeCoordinator() -> Coordinator {
diff --git a/ListlessiOS/Views/TaskListView.swift b/ListlessiOS/Views/TaskListView.swift
@@ -295,7 +295,8 @@ struct TaskListView: View, TaskListViewProtocol {
}
}
},
- returnKeyType: .done
+ returnKeyType: .done,
+ uiAccessibilityIdentifier: "draft-row-prepend"
)
.focused($focusedFieldBinding, equals: .task(draftPrependRowID))
.frame(maxWidth: .infinity, alignment: .leading)
@@ -361,7 +362,8 @@ struct TaskListView: View, TaskListViewProtocol {
},
returnKeyType: draftTitle.trimmingCharacters(
in: .whitespacesAndNewlines
- ).isEmpty ? .done : .next
+ ).isEmpty ? .done : .next,
+ uiAccessibilityIdentifier: "draft-row-append"
)
.focused($focusedFieldBinding, equals: .task(draftAppendRowID))
.frame(maxWidth: .infinity, alignment: .leading)
diff --git a/ListlessiOS/Views/TaskRowView.swift b/ListlessiOS/Views/TaskRowView.swift
@@ -71,6 +71,8 @@ struct TaskRowView: View {
.font(.system(size: 17))
}
.buttonStyle(.borderless)
+ .accessibilityIdentifier("task-checkbox")
+ .accessibilityValue(task.isCompleted ? "checkmark.circle.fill" : "circle")
TappableTextField(
text: $editingTitle,
@@ -89,7 +91,8 @@ struct TaskRowView: View {
onContentChange: { newTitle in
guard !task.isCompleted else { return }
onTitleChange(task, newTitle)
- }
+ },
+ uiAccessibilityIdentifier: "task-text-\(taskID.uuidString)"
)
.focused($focusedField, equals: .task(taskID))
.frame(maxWidth: .infinity, alignment: .leading)
diff --git a/Scripts/test-ios-ui.sh b/Scripts/test-ios-ui.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -euo pipefail
+
+xcodebuild test \
+ -scheme "Listless iOS" \
+ -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.6' \
+ -only-testing:"Listless iOS UI Tests" \
+ 2>&1
diff --git a/Tests/UI/ListlessiOSUITests.swift b/Tests/UI/ListlessiOSUITests.swift
@@ -0,0 +1,169 @@
+import XCTest
+
+final class ListlessiOSUITests: XCTestCase {
+ var app: XCUIApplication!
+
+ override func setUpWithError() throws {
+ continueAfterFailure = false
+ app = XCUIApplication()
+ app.launchArguments = ["UI_TESTING"]
+ app.launch()
+ }
+
+ override func tearDownWithError() throws {
+ app.terminate()
+ }
+
+ // MARK: - Helpers
+
+ /// The "Pull down to create" empty state label.
+ var emptyStateLabel: XCUIElement {
+ app.staticTexts["Pull down to create"]
+ }
+
+ /// The main scroll view area; tapping empty space here creates a draft task.
+ var taskListScrollView: XCUIElement {
+ app.scrollViews["task-list-scrollview"]
+ }
+
+ /// The draft row text view that appears after tapping empty space.
+ /// TappableTextField wraps UITextView, so XCUITest sees it as a textView.
+ var draftTextField: XCUIElement {
+ app.textViews.matching(identifier: "draft-row-append").firstMatch
+ }
+
+ /// Returns the text view for a committed task with the given title.
+ func taskText(_ title: String) -> XCUIElement {
+ app.textViews.matching(
+ NSPredicate(format: "identifier BEGINSWITH 'task-text-' AND value == %@", title)
+ ).firstMatch
+ }
+
+ /// Creates a task by tapping empty space to reveal the draft field,
+ /// typing a title, and pressing Return.
+ func createTask(_ title: String) {
+ let textView = draftTextField
+ if !textView.exists {
+ taskListScrollView.tap()
+ if !textView.waitForExistence(timeout: 2) {
+ XCTFail("Draft text view should appear after tapping empty space")
+ return
+ }
+ }
+ textView.tap()
+ textView.typeText(title + "\n")
+ }
+
+ /// Returns the Nth checkbox button (0-indexed).
+ func taskCheckbox(at index: Int) -> XCUIElement {
+ app.buttons.matching(identifier: "task-checkbox").element(boundBy: index)
+ }
+
+ /// Exits editing mode. After createTask, the new draft text view is
+ /// focused. Dismiss it by tapping the scroll view background (which
+ /// calls handleBackgroundTap to commit/dismiss the empty draft).
+ func exitEditingMode() {
+ let draft = draftTextField
+ if draft.exists {
+ draft.typeText("\n")
+ }
+ // Tap background to deselect
+ taskListScrollView.tap()
+ }
+
+ // MARK: - Empty State
+
+ func testLaunchShowsEmptyState() {
+ XCTAssertTrue(
+ emptyStateLabel.waitForExistence(timeout: 2),
+ "Empty state label should be visible on launch"
+ )
+ }
+
+ func testEmptyStateDisappearsAfterCreatingTask() {
+ createTask("First item")
+ exitEditingMode()
+ XCTAssertFalse(emptyStateLabel.exists, "Empty state should disappear after creating a task")
+ }
+
+ // MARK: - Task Creation
+
+ func testCreateTaskViaTap() {
+ createTask("Buy groceries")
+ exitEditingMode()
+ XCTAssertTrue(
+ taskText("Buy groceries").waitForExistence(timeout: 2),
+ "Task should appear with the typed title"
+ )
+ }
+
+ func testReturnChainsNewTask() {
+ createTask("First item")
+ XCTAssertTrue(
+ draftTextField.waitForExistence(timeout: 2),
+ "New draft text view should appear after Return"
+ )
+ }
+
+ func testCreateMultipleTasks() {
+ createTask("Alpha")
+ createTask("Bravo")
+ createTask("Charlie")
+ exitEditingMode()
+
+ XCTAssertTrue(taskText("Alpha").waitForExistence(timeout: 2))
+ XCTAssertTrue(taskText("Bravo").exists)
+ XCTAssertTrue(taskText("Charlie").exists)
+ }
+
+ func testEmptyTaskDeletedOnCommit() {
+ taskListScrollView.tap()
+ XCTAssertTrue(draftTextField.waitForExistence(timeout: 2))
+ draftTextField.typeText("\n")
+ XCTAssertTrue(
+ emptyStateLabel.waitForExistence(timeout: 2),
+ "Empty state should reappear when empty task is discarded"
+ )
+ }
+
+ // MARK: - Task Completion
+
+ func testCompleteTaskViaCheckbox() {
+ createTask("Finish report")
+ exitEditingMode()
+
+ let checkbox = taskCheckbox(at: 0)
+ XCTAssertTrue(checkbox.waitForExistence(timeout: 2))
+ XCTAssertEqual(checkbox.value as? String, "circle")
+ checkbox.tap()
+
+ let completed = app.buttons.matching(
+ NSPredicate(format: "identifier == 'task-checkbox' AND value == 'checkmark.circle.fill'")
+ ).firstMatch
+ XCTAssertTrue(
+ completed.waitForExistence(timeout: 3),
+ "Checkbox should show checkmark after tapping"
+ )
+ }
+
+ func testUncompleteTask() {
+ createTask("Finish report")
+ exitEditingMode()
+
+ taskCheckbox(at: 0).tap()
+
+ let completed = app.buttons.matching(
+ NSPredicate(format: "identifier == 'task-checkbox' AND value == 'checkmark.circle.fill'")
+ ).firstMatch
+ XCTAssertTrue(completed.waitForExistence(timeout: 3))
+ completed.tap()
+
+ let uncompleted = app.buttons.matching(
+ NSPredicate(format: "identifier == 'task-checkbox' AND value == 'circle'")
+ ).firstMatch
+ XCTAssertTrue(
+ uncompleted.waitForExistence(timeout: 3),
+ "Checkbox should revert to circle after uncompleting"
+ )
+ }
+}
diff --git a/project.yml b/project.yml
@@ -176,11 +176,27 @@ targets:
BUNDLE_LOADER: $(TEST_HOST)
GENERATE_INFOPLIST_FILE: YES
+ Listless iOS UI Tests:
+ type: bundle.ui-testing
+ platform: iOS
+ sources:
+ - path: Tests/UI
+ includes:
+ - "ListlessiOSUITests.swift"
+ dependencies:
+ - target: Listless iOS
+ settings:
+ PRODUCT_BUNDLE_IDENTIFIER: net.inqk.listless.ios.uitests
+ CODE_SIGN_STYLE: Automatic
+ GENERATE_INFOPLIST_FILE: YES
+
Listless macOS UI Tests:
type: bundle.ui-testing
platform: macOS
sources:
- - Tests/UI
+ - path: Tests/UI
+ includes:
+ - "ListlessMacUITests.swift"
dependencies:
- target: Listless macOS
settings:
@@ -208,6 +224,9 @@ schemes:
- name: Listless iOS Unit Tests
parallelizable: true
randomExecutionOrder: true
+ - name: Listless iOS UI Tests
+ parallelizable: true
+ randomExecutionOrder: true
profile:
config: Release
analyze: