Enable parallel UI test execution via per-session data isolation
Each test class now gets a unique session ID (UUID) passed to the app via UI_TEST_SESSION_ID environment variable. The app uses this to: - Route GroupUserDefaults to a session-specific UserDefaults suite, preventing tests from clobbering each other's AppStorage state - Create an in-memory SwiftData container instead of the shared on-disk App Group store, eliminating SQLite contention Refactored 8 test classes that bypassed BaseUITestCase.setUp() with custom launch args — they now use overridable `localeArguments` and `extraLaunchArguments` properties, keeping session ID injection centralized. Added `relaunchApp(resetState:bypassSubscription:)` to BaseUITestCase for tests that need mid-test relaunch with different subscription state. Includes a ParallelUITests.xctestplan with class-level parallelism enabled and random execution ordering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"configurations" : [
|
||||||
|
{
|
||||||
|
"id" : "9A1B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D",
|
||||||
|
"name" : "Parallel UI Tests",
|
||||||
|
"options" : {
|
||||||
|
"testExecutionOrdering" : "random"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"defaultOptions" : {
|
||||||
|
"maximumTestExecutionTimeAllowance" : 180,
|
||||||
|
"testTimeoutsEnabled" : true
|
||||||
|
},
|
||||||
|
"testTargets" : [
|
||||||
|
{
|
||||||
|
"parallelizable" : true,
|
||||||
|
"target" : {
|
||||||
|
"containerPath" : "container:Reflect.xcodeproj",
|
||||||
|
"identifier" : "Tests iOS",
|
||||||
|
"name" : "Tests iOS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
@@ -36,6 +36,15 @@ enum SharedModelContainer {
|
|||||||
/// - Returns: Configured ModelContainer
|
/// - Returns: Configured ModelContainer
|
||||||
/// - Throws: SharedModelContainerError if creation fails
|
/// - Throws: SharedModelContainerError if creation fails
|
||||||
static func create(useCloudKit: Bool = true) throws -> ModelContainer {
|
static func create(useCloudKit: Bool = true) throws -> ModelContainer {
|
||||||
|
// When UI testing, use in-memory storage for parallel test isolation.
|
||||||
|
// Each test process gets its own empty container — no shared on-disk state.
|
||||||
|
// Check ProcessInfo directly to avoid depending on UITestMode (not in widget targets).
|
||||||
|
if ProcessInfo.processInfo.arguments.contains("--ui-testing") {
|
||||||
|
let schema = Schema([MoodEntryModel.self])
|
||||||
|
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true, cloudKitDatabase: .none)
|
||||||
|
return try ModelContainer(for: schema, configurations: [config])
|
||||||
|
}
|
||||||
|
|
||||||
let schema = Schema([MoodEntryModel.self])
|
let schema = Schema([MoodEntryModel.self])
|
||||||
let storeURL = try Self.storeURL
|
let storeURL = try Self.storeURL
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,26 @@ struct Constants {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct GroupUserDefaults {
|
struct GroupUserDefaults {
|
||||||
|
/// Whether the current process is a UI test session with an isolation ID.
|
||||||
|
/// Inlined from ProcessInfo to avoid depending on UITestMode (which isn't in widget targets).
|
||||||
|
private static var uiTestSessionID: String? {
|
||||||
|
guard ProcessInfo.processInfo.arguments.contains("--ui-testing") else { return nil }
|
||||||
|
return ProcessInfo.processInfo.environment["UI_TEST_SESSION_ID"]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The suite name currently in use. Used by resetAppState() to clear the correct domain.
|
||||||
|
static var currentSuiteName: String {
|
||||||
|
if let sessionID = uiTestSessionID {
|
||||||
|
return "uitest.\(sessionID)"
|
||||||
|
}
|
||||||
|
return Constants.currentGroupShareId
|
||||||
|
}
|
||||||
|
|
||||||
static var groupDefaults: UserDefaults {
|
static var groupDefaults: UserDefaults {
|
||||||
|
// When UI testing with a session ID, use a per-session suite for parallel isolation.
|
||||||
|
if let sessionID = uiTestSessionID {
|
||||||
|
return UserDefaults(suiteName: "uitest.\(sessionID)") ?? .standard
|
||||||
|
}
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
return UserDefaults(suiteName: Constants.groupShareIdDebug) ?? .standard
|
return UserDefaults(suiteName: Constants.groupShareIdDebug) ?? .standard
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ enum UITestMode {
|
|||||||
ProcessInfo.processInfo.arguments.contains("--expire-trial")
|
ProcessInfo.processInfo.arguments.contains("--expire-trial")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unique session ID for parallel test isolation.
|
||||||
|
/// Each test class gets its own session, ensuring no shared state between parallel test runners.
|
||||||
|
static var sessionID: String? {
|
||||||
|
ProcessInfo.processInfo.environment["UI_TEST_SESSION_ID"]
|
||||||
|
}
|
||||||
|
|
||||||
/// Seed fixture name if provided (via environment variable)
|
/// Seed fixture name if provided (via environment variable)
|
||||||
static var seedFixture: String? {
|
static var seedFixture: String? {
|
||||||
ProcessInfo.processInfo.environment["UI_TEST_FIXTURE"]
|
ProcessInfo.processInfo.environment["UI_TEST_FIXTURE"]
|
||||||
@@ -93,8 +99,9 @@ enum UITestMode {
|
|||||||
@MainActor
|
@MainActor
|
||||||
private static func resetAppState() {
|
private static func resetAppState() {
|
||||||
let defaults = GroupUserDefaults.groupDefaults
|
let defaults = GroupUserDefaults.groupDefaults
|
||||||
// Clear group user defaults using the suite domain name
|
// Clear group user defaults using the session-specific or shared suite domain name
|
||||||
defaults.removePersistentDomain(forName: Constants.currentGroupShareId)
|
let suiteName = GroupUserDefaults.currentSuiteName
|
||||||
|
defaults.removePersistentDomain(forName: suiteName)
|
||||||
|
|
||||||
// Explicitly clear subscription cache keys that may survive removePersistentDomain
|
// Explicitly clear subscription cache keys that may survive removePersistentDomain
|
||||||
// on app group suites (known reliability issue).
|
// on app group suites (known reliability issue).
|
||||||
|
|||||||
@@ -10,25 +10,8 @@ import XCTest
|
|||||||
final class AccessibilityTextSizeTests: BaseUITestCase {
|
final class AccessibilityTextSizeTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "single_mood" }
|
override var seedFixture: String? { "single_mood" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var extraLaunchArguments: [String] {
|
||||||
override func setUp() {
|
["-UIPreferredContentSizeCategoryName", "UICTContentSizeCategoryAccessibilityXXL"]
|
||||||
// Do NOT call super — we need custom content size launch args
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
var args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(en)",
|
|
||||||
"-AppleLocale", "en_US",
|
|
||||||
"-UIPreferredContentSizeCategoryName", "UICTContentSizeCategoryAccessibilityXXL"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "single_mood"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TC-142: App launches and is navigable at largest accessibility text size.
|
/// TC-142: App launches and is navigable at largest accessibility text size.
|
||||||
|
|||||||
@@ -10,25 +10,7 @@ import XCTest
|
|||||||
final class DateLocaleTests: BaseUITestCase {
|
final class DateLocaleTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "week_of_moods" }
|
override var seedFixture: String? { "week_of_moods" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var localeArguments: [String] { ["-AppleLanguages", "(de)", "-AppleLocale", "de_DE"] }
|
||||||
override func setUp() {
|
|
||||||
// Do NOT call super — we need custom locale launch args
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
let args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(de)",
|
|
||||||
"-AppleLocale", "de_DE"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "week_of_moods"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TC-139: German locale displays German month/weekday names.
|
/// TC-139: German locale displays German month/weekday names.
|
||||||
func testGermanLocale_DateFormattingMatchesLocale() {
|
func testGermanLocale_DateFormattingMatchesLocale() {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
// Tests iOS
|
// Tests iOS
|
||||||
//
|
//
|
||||||
// Base class for all UI tests. Handles launch arguments,
|
// Base class for all UI tests. Handles launch arguments,
|
||||||
// state reset, and screenshot capture on failure.
|
// state reset, screenshot capture on failure, and parallel
|
||||||
|
// test isolation via per-session data sandboxing.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@@ -12,6 +13,13 @@ class BaseUITestCase: XCTestCase {
|
|||||||
|
|
||||||
var app: XCUIApplication!
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
// MARK: - Parallel Test Isolation
|
||||||
|
|
||||||
|
/// Unique session ID for this test class instance.
|
||||||
|
/// Passed to the app via environment so each parallel runner gets
|
||||||
|
/// its own UserDefaults suite and in-memory SwiftData container.
|
||||||
|
private(set) var testSessionID: String = UUID().uuidString
|
||||||
|
|
||||||
// MARK: - Configuration (override in subclasses)
|
// MARK: - Configuration (override in subclasses)
|
||||||
|
|
||||||
/// Fixture to seed. Override to use a specific data set.
|
/// Fixture to seed. Override to use a specific data set.
|
||||||
@@ -26,6 +34,15 @@ class BaseUITestCase: XCTestCase {
|
|||||||
/// Whether to force the trial to be expired. Default: false.
|
/// Whether to force the trial to be expired. Default: false.
|
||||||
var expireTrial: Bool { false }
|
var expireTrial: Bool { false }
|
||||||
|
|
||||||
|
/// Override to change the test locale/language.
|
||||||
|
/// Default: English (US). Locale tests override this instead of setUp().
|
||||||
|
var localeArguments: [String] { ["-AppleLanguages", "(en)", "-AppleLocale", "en_US"] }
|
||||||
|
|
||||||
|
/// Extra launch arguments for tests needing special settings
|
||||||
|
/// (accessibility sizes, reduce motion, high contrast, etc.).
|
||||||
|
/// Override in subclasses instead of overriding setUp().
|
||||||
|
var extraLaunchArguments: [String] { [] }
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
@@ -46,7 +63,8 @@ class BaseUITestCase: XCTestCase {
|
|||||||
// MARK: - Launch Configuration
|
// MARK: - Launch Configuration
|
||||||
|
|
||||||
private func buildLaunchArguments(resetState: Bool) -> [String] {
|
private func buildLaunchArguments(resetState: Bool) -> [String] {
|
||||||
var args = ["--ui-testing", "--disable-animations", "-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
|
var args = ["--ui-testing", "--disable-animations"]
|
||||||
|
args.append(contentsOf: localeArguments)
|
||||||
if resetState {
|
if resetState {
|
||||||
args.append("--reset-state")
|
args.append("--reset-state")
|
||||||
}
|
}
|
||||||
@@ -59,11 +77,13 @@ class BaseUITestCase: XCTestCase {
|
|||||||
if expireTrial {
|
if expireTrial {
|
||||||
args.append("--expire-trial")
|
args.append("--expire-trial")
|
||||||
}
|
}
|
||||||
|
args.append(contentsOf: extraLaunchArguments)
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildLaunchEnvironment() -> [String: String] {
|
private func buildLaunchEnvironment() -> [String: String] {
|
||||||
var env = [String: String]()
|
var env = [String: String]()
|
||||||
|
env["UI_TEST_SESSION_ID"] = testSessionID
|
||||||
if let fixture = seedFixture {
|
if let fixture = seedFixture {
|
||||||
env["UI_TEST_FIXTURE"] = fixture
|
env["UI_TEST_FIXTURE"] = fixture
|
||||||
}
|
}
|
||||||
@@ -90,6 +110,26 @@ class BaseUITestCase: XCTestCase {
|
|||||||
return application
|
return application
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Relaunch the app with custom bypass setting, preserving the session ID.
|
||||||
|
/// Use when a test needs to toggle subscription bypass mid-test.
|
||||||
|
@discardableResult
|
||||||
|
func relaunchApp(resetState: Bool, bypassSubscription overrideBypass: Bool) -> XCUIApplication {
|
||||||
|
app.terminate()
|
||||||
|
let application = XCUIApplication()
|
||||||
|
var args = ["--ui-testing", "--disable-animations"]
|
||||||
|
args.append(contentsOf: localeArguments)
|
||||||
|
if resetState { args.append("--reset-state") }
|
||||||
|
if overrideBypass { args.append("--bypass-subscription") }
|
||||||
|
if skipOnboarding { args.append("--skip-onboarding") }
|
||||||
|
if expireTrial { args.append("--expire-trial") }
|
||||||
|
args.append(contentsOf: extraLaunchArguments)
|
||||||
|
application.launchArguments = args
|
||||||
|
application.launchEnvironment = buildLaunchEnvironment()
|
||||||
|
application.launch()
|
||||||
|
app = application
|
||||||
|
return application
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func relaunchPreservingState() -> XCUIApplication {
|
func relaunchPreservingState() -> XCUIApplication {
|
||||||
app.terminate()
|
app.terminate()
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class HierarchyDumpTest: XCTestCase {
|
class HierarchyDumpTest: BaseUITestCase {
|
||||||
|
override var seedFixture: String? { nil }
|
||||||
|
|
||||||
func testDumpAccessibilityTree() {
|
func testDumpAccessibilityTree() {
|
||||||
let app = XCUIApplication()
|
|
||||||
app.launchArguments = ["--ui-testing", "--reset-state", "--disable-animations", "--bypass-subscription", "--skip-onboarding"]
|
|
||||||
app.launch()
|
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
print("\n=== ELEMENT QUERIES ===")
|
print("\n=== ELEMENT QUERIES ===")
|
||||||
print("otherElements[mood_header]: \(app.otherElements[\"mood_header\"].exists)")
|
print("otherElements[mood_header]: \(app.otherElements["mood_header"].exists)")
|
||||||
print("descendants[mood_header]: \(app.descendants(matching: .any)[\"mood_header\"].firstMatch.exists)")
|
print("descendants[mood_header]: \(app.descendants(matching: .any)["mood_header"].firstMatch.exists)")
|
||||||
print("groups[mood_header]: \(app.groups[\"mood_header\"].exists)")
|
print("groups[mood_header]: \(app.groups["mood_header"].exists)")
|
||||||
print("scrollViews[mood_header]: \(app.scrollViews[\"mood_header\"].exists)")
|
print("scrollViews[mood_header]: \(app.scrollViews["mood_header"].exists)")
|
||||||
print("staticTexts[mood_header]: \(app.staticTexts[\"mood_header\"].exists)")
|
print("staticTexts[mood_header]: \(app.staticTexts["mood_header"].exists)")
|
||||||
print("buttons[mood_button_great]: \(app.buttons[\"mood_button_great\"].exists)")
|
print("buttons[mood_button_great]: \(app.buttons["mood_button_great"].exists)")
|
||||||
print("tabBars count: \(app.tabBars.count)")
|
print("tabBars count: \(app.tabBars.count)")
|
||||||
if app.tabBars.count > 0 {
|
if app.tabBars.count > 0 {
|
||||||
let tb = app.tabBars.firstMatch
|
let tb = app.tabBars.firstMatch
|
||||||
@@ -21,7 +20,7 @@ class HierarchyDumpTest: XCTestCase {
|
|||||||
print(" tab button: \(b.identifier) label=\(b.label)")
|
print(" tab button: \(b.identifier) label=\(b.label)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print("otherElements[settings_header]: \(app.otherElements[\"settings_header\"].exists)")
|
print("otherElements[settings_header]: \(app.otherElements["settings_header"].exists)")
|
||||||
|
|
||||||
print("\n=== HIERARCHY (first 200 lines) ===")
|
print("\n=== HIERARCHY (first 200 lines) ===")
|
||||||
let desc = app.debugDescription
|
let desc = app.debugDescription
|
||||||
|
|||||||
@@ -10,25 +10,8 @@ import XCTest
|
|||||||
final class HighContrastTests: BaseUITestCase {
|
final class HighContrastTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "single_mood" }
|
override var seedFixture: String? { "single_mood" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var extraLaunchArguments: [String] {
|
||||||
override func setUp() {
|
["-UIAccessibilityDarkerSystemColorsEnabled", "YES"]
|
||||||
// Do NOT call super — we need custom accessibility launch args
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
let args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(en)",
|
|
||||||
"-AppleLocale", "en_US",
|
|
||||||
"-UIAccessibilityDarkerSystemColorsEnabled", "YES"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "single_mood"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TC-144: App is navigable with High Contrast mode enabled.
|
/// TC-144: App is navigable with High Contrast mode enabled.
|
||||||
|
|||||||
@@ -10,25 +10,7 @@ import XCTest
|
|||||||
final class LongTranslationTests: BaseUITestCase {
|
final class LongTranslationTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "single_mood" }
|
override var seedFixture: String? { "single_mood" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var localeArguments: [String] { ["-AppleLanguages", "(de)", "-AppleLocale", "de_DE"] }
|
||||||
override func setUp() {
|
|
||||||
// Do NOT call super — we need German locale (known for long compound words)
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
let args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(de)",
|
|
||||||
"-AppleLocale", "de_DE"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "single_mood"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TC-138: German locale with long compound words renders without crashes.
|
/// TC-138: German locale with long compound words renders without crashes.
|
||||||
/// Navigates through all tabs to ensure no layout truncation causes issues.
|
/// Navigates through all tabs to ensure no layout truncation causes issues.
|
||||||
|
|||||||
@@ -10,25 +10,8 @@ import XCTest
|
|||||||
final class ReduceMotionTests: BaseUITestCase {
|
final class ReduceMotionTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "single_mood" }
|
override var seedFixture: String? { "single_mood" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var extraLaunchArguments: [String] {
|
||||||
override func setUp() {
|
["-UIReduceMotionPreference", "YES"]
|
||||||
// Do NOT call super — we need custom accessibility launch args
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
let args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(en)",
|
|
||||||
"-AppleLocale", "en_US",
|
|
||||||
"-UIReduceMotionPreference", "YES"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "single_mood"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TC-143: App is navigable with Reduce Motion enabled.
|
/// TC-143: App is navigable with Reduce Motion enabled.
|
||||||
|
|||||||
@@ -10,25 +10,7 @@ import XCTest
|
|||||||
final class SpanishLocalizationTests: BaseUITestCase {
|
final class SpanishLocalizationTests: BaseUITestCase {
|
||||||
override var seedFixture: String? { "week_of_moods" }
|
override var seedFixture: String? { "week_of_moods" }
|
||||||
override var bypassSubscription: Bool { true }
|
override var bypassSubscription: Bool { true }
|
||||||
|
override var localeArguments: [String] { ["-AppleLanguages", "(es)", "-AppleLocale", "es_ES"] }
|
||||||
override func setUp() {
|
|
||||||
// Do NOT call super — we need custom language launch args
|
|
||||||
continueAfterFailure = false
|
|
||||||
|
|
||||||
let application = XCUIApplication()
|
|
||||||
let args: [String] = [
|
|
||||||
"--ui-testing", "--disable-animations",
|
|
||||||
"--reset-state",
|
|
||||||
"--bypass-subscription",
|
|
||||||
"--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(es)",
|
|
||||||
"-AppleLocale", "es_ES"
|
|
||||||
]
|
|
||||||
application.launchArguments = args
|
|
||||||
application.launchEnvironment = ["UI_TEST_FIXTURE": "week_of_moods"]
|
|
||||||
application.launch()
|
|
||||||
app = application
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TC-137: Key Spanish strings appear when launched in Spanish locale.
|
/// TC-137: Key Spanish strings appear when launched in Spanish locale.
|
||||||
func testSpanishLocale_DisplaysSpanishStrings() {
|
func testSpanishLocale_DisplaysSpanishStrings() {
|
||||||
|
|||||||
@@ -20,20 +20,8 @@ final class TrialBannerTests: BaseUITestCase {
|
|||||||
settingsScreen.assertVisible()
|
settingsScreen.assertVisible()
|
||||||
|
|
||||||
// With default settings (bypassSubscription = true), the banner is hidden.
|
// With default settings (bypassSubscription = true), the banner is hidden.
|
||||||
// We need to launch without bypass to see the banner.
|
// Re-launch without bypass to see the banner.
|
||||||
// Re-launch with bypass disabled.
|
relaunchApp(resetState: true, bypassSubscription: false)
|
||||||
app.terminate()
|
|
||||||
|
|
||||||
let freshApp = XCUIApplication()
|
|
||||||
var args = ["--ui-testing", "--reset-state", "--disable-animations", "--skip-onboarding",
|
|
||||||
"-AppleLanguages", "(en)", "-AppleLocale", "en_US"]
|
|
||||||
// Do NOT add --bypass-subscription
|
|
||||||
freshApp.launchArguments = args
|
|
||||||
if let fixture = seedFixture {
|
|
||||||
freshApp.launchEnvironment = ["UI_TEST_FIXTURE": fixture]
|
|
||||||
}
|
|
||||||
freshApp.launch()
|
|
||||||
app = freshApp
|
|
||||||
|
|
||||||
// Navigate to Settings
|
// Navigate to Settings
|
||||||
let freshTabBar = TabBarScreen(app: app)
|
let freshTabBar = TabBarScreen(app: app)
|
||||||
|
|||||||
Reference in New Issue
Block a user