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.
This commit is contained in:
@@ -7,6 +7,7 @@ import XCTest
|
||||
|
||||
/// Detail sheet, editing, disabling, deleting, and unblocking — seeded with
|
||||
/// one actively-blocking rule ("Work Time") and one upcoming rule ("Sleep").
|
||||
/// Rule cards live on the Rules tab; blocked tiles on the Home tab.
|
||||
final class RuleManagementUITests: XCTestCase {
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
@@ -14,6 +15,7 @@ final class RuleManagementUITests: XCTestCase {
|
||||
|
||||
func testDetailShowsLiveStatusAndFacts() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "standard")
|
||||
app.goToRulesTab()
|
||||
|
||||
app.buttons["ruleCard-Work Time"].waitToAppear().tap()
|
||||
|
||||
@@ -30,6 +32,7 @@ final class RuleManagementUITests: XCTestCase {
|
||||
|
||||
func testEditRuleTogglesHardModeOn() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "standard")
|
||||
app.goToRulesTab()
|
||||
|
||||
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
|
||||
app.buttons["editRuleButton"].waitToAppear().tap()
|
||||
@@ -44,6 +47,7 @@ final class RuleManagementUITests: XCTestCase {
|
||||
|
||||
func testDisableRule() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "standard")
|
||||
app.goToRulesTab()
|
||||
|
||||
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
|
||||
app.buttons["editRuleButton"].waitToAppear().tap()
|
||||
@@ -65,6 +69,7 @@ final class RuleManagementUITests: XCTestCase {
|
||||
|
||||
func testDeleteRuleRemovesCard() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "standard")
|
||||
app.goToRulesTab()
|
||||
|
||||
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
|
||||
app.buttons["editRuleButton"].waitToAppear().tap()
|
||||
@@ -84,11 +89,13 @@ final class RuleManagementUITests: XCTestCase {
|
||||
func testUnblockActiveSoftRule() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "standard")
|
||||
|
||||
// The active rule surfaces in Blocked Apps; unblocking pauses it.
|
||||
// The active rule surfaces in Currently Blocking (Home tab); unblocking pauses it.
|
||||
app.buttons["blockedTile-Work Time"].waitToAppear().tap()
|
||||
app.sheets.buttons["Unblock"].waitToAppear().tap()
|
||||
|
||||
app.staticTexts["nothingBlockedLabel"].waitToAppear()
|
||||
// The paused state shows on the rule's card over on the Rules tab.
|
||||
app.goToRulesTab()
|
||||
XCTAssertEqual(app.staticTexts["ruleStatus-Work Time"].waitToAppear().label, "Paused")
|
||||
}
|
||||
}
|
||||
@@ -101,6 +108,7 @@ final class HardModeUITests: XCTestCase {
|
||||
|
||||
func testHardLockedRuleCannotBeEdited() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "hard-mode-active")
|
||||
app.goToRulesTab()
|
||||
|
||||
app.buttons["ruleCard-Locked In"].waitToAppear().tap()
|
||||
|
||||
@@ -113,6 +121,8 @@ final class HardModeUITests: XCTestCase {
|
||||
func testHardLockedRuleCannotBeUnblocked() throws {
|
||||
let app = XCUIApplication.launchOpenAppLock(seedScenario: "hard-mode-active")
|
||||
|
||||
// The hard rule shows a lock (not an Unblock button) in Currently Blocking;
|
||||
// tapping the row still explains why it can't be lifted.
|
||||
app.buttons["blockedTile-Locked In"].waitToAppear().tap()
|
||||
|
||||
// No unblock dialog — just the refusal alert.
|
||||
|
||||
Reference in New Issue
Block a user