Files
OpenAppLock/OpenAppLockUITests/UITestSupport.swift
Brendan Chen a658d196c4 feat: re-skin to native three-tab UI and add Uninstall Protection
Replace the custom themed presentation with the bare native iOS design
language across a three-tab TabView (Home / Rules / Settings):

- MainTabView hosts the enforcement lifecycle (refresh loop, rule-change
  and scene-active reconcile) so it runs regardless of the selected tab
- HomeView: "Currently Blocking" + "Usage" sections (replaces AppsHomeView)
- RulesListView: rules grouped into Schedule / Time Limit / Open Limit
- SettingsView: Uninstall Protection toggle + Manage App Lists
- AppListLibraryView / ManageAppListsView: shared App List management
- AppSettings: app-group-backed settings store

Uninstall Protection: while enabled and any Hard Mode rule is actively
blocking, deny device app-removal via a dedicated ManagedSettingsStore
(RulePolicy.shouldDenyAppRemoval -> RuleEnforcer -> ShieldController).

Slim AppListPickerSheet to the picker role; drop AppsHomeView and the
custom Theme/HoldToCommitButton/RuleCardView chrome. Update spec section 6
and the unit/UI test suites accordingly.
2026-06-13 22:17:08 -04:00

59 lines
2.0 KiB
Swift

//
// UITestSupport.swift
// OpenAppLockUITests
//
import XCTest
extension XCUIApplication {
/// Launches the app in UI-testing mode: in-memory storage, mocked Screen
/// Time authorization, and no shield side effects.
static func launchOpenAppLock(
onboardingCompleted: Bool = true,
seedScenario: String? = nil
) -> XCUIApplication {
let app = XCUIApplication()
var arguments = ["-ui-testing"]
arguments.append(onboardingCompleted ? "-onboarding-completed" : "-onboarding-required")
if let seedScenario {
arguments.append("-seed-scenario=\(seedScenario)")
}
app.launchArguments = arguments
app.launch()
return app
}
}
extension XCUIApplication {
/// Finds an element by accessibility identifier regardless of how SwiftUI
/// exposes it (Other, StaticText, Button, ).
func element(_ identifier: String) -> XCUIElement {
descendants(matching: .any).matching(identifier: identifier).firstMatch
}
}
extension XCUIApplication {
/// Switches to the Home tab (Currently Blocking + Usage). Home is the
/// default selection, so most Home-tab tests don't need to call this.
func goToHomeTab() { tabBars.buttons["Home"].waitToAppear().tap() }
/// Switches to the Rules tab (the rule list + New Rule button).
func goToRulesTab() { tabBars.buttons["Rules"].waitToAppear().tap() }
/// Switches to the Settings tab (Uninstall Protection + Manage App Lists).
func goToSettingsTab() { tabBars.buttons["Settings"].waitToAppear().tap() }
}
extension XCUIElement {
/// Asserts the element appears within the timeout, then returns it.
@discardableResult
func waitToAppear(
timeout: TimeInterval = 5, file: StaticString = #filePath, line: UInt = #line
) -> XCUIElement {
XCTAssertTrue(
waitForExistence(timeout: timeout),
"Expected \(self) to exist within \(timeout)s",
file: file, line: line
)
return self
}
}