Files
OpenAppLock/SeveredUITests/RuleManagementUITests.swift
Brendan Chen 4ee8f01d21 fix: editor usability and visual polish from UI scan
- Rule name is now an inline text field at the top of the editor; the
  pencil/rename button and alert are gone (it was unclear what the edit
  button did). Names are sanitized on commit: trimmed, falling back to
  the kind's default when emptied
- Day-of-week toggles fill the full row width with equal cells and
  >= 44pt tap targets
- 'Add Rule' commit button sits on a bar-material bottom inset so form
  content no longer collides with it while scrolling (found via
  computer-use scan; verified clean in a follow-up scan)
- Tests: 95 passing — new specs for the inline rename flow, day-toggle
  geometry, and name sanitization; three tests now scroll to reach
  bottom-of-form controls, matching real user behavior
- Spec §6 updated for the editor changes
2026-06-12 15:44:25 -04:00

135 lines
5.3 KiB
Swift

//
// RuleManagementUITests.swift
// SeveredUITests
//
import XCTest
/// Detail sheet, editing, disabling, deleting, and unblocking seeded with
/// one actively-blocking rule ("Work Time") and one upcoming rule ("Sleep").
final class RuleManagementUITests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
func testDetailShowsLiveStatusAndFacts() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
app.buttons["ruleCard-Work Time"].waitToAppear().tap()
XCTAssertEqual(app.staticTexts["detailRuleName"].waitToAppear().label, "Work Time")
XCTAssertTrue(app.staticTexts["detailStatusLabel"].label.contains("left"))
app.element("detailRow-During this time").waitToAppear()
XCTAssertTrue(app.element("detailRow-On these days").exists)
XCTAssertTrue(app.element("detailRow-Unblocks allowed").exists)
app.buttons["editRuleButton"].waitToAppear()
app.buttons["closeDetailButton"].tap()
app.buttons["newRuleButton"].waitToAppear()
}
func testEditRuleTogglesHardModeOn() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
app.buttons["editRuleButton"].waitToAppear().tap()
app.switches["hardModeToggle"].waitToAppear().tap()
app.buttons["doneButton"].tap()
// Back on the detail view, unblocks are no longer allowed.
let row = app.element("detailRow-Unblocks allowed").waitToAppear()
XCTAssertTrue(row.label.contains("No"), "Expected 'Unblocks allowed: No', got: \(row.label)")
}
func testDisableRule() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
app.buttons["editRuleButton"].waitToAppear().tap()
// The disable/delete rows sit at the bottom of the form.
app.staticTexts["ruleEditorTitle"].waitToAppear()
app.swipeUp()
app.buttons["toggleEnabledButton"].waitToAppear().tap()
// The detail caption now reports the rule as disabled.
let status = app.staticTexts["detailStatusLabel"].waitToAppear()
XCTAssertTrue(status.label.contains("Disabled"), "Got: \(status.label)")
app.buttons["closeDetailButton"].tap()
let cardStatus = app.staticTexts["ruleStatus-Sleep"].waitToAppear()
XCTAssertEqual(cardStatus.label, "Disabled")
}
func testDeleteRuleRemovesCard() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
app.buttons["ruleCard-Sleep"].waitToAppear().tap()
app.buttons["editRuleButton"].waitToAppear().tap()
// The disable/delete rows sit at the bottom of the form.
app.staticTexts["ruleEditorTitle"].waitToAppear()
app.swipeUp()
app.buttons["deleteRuleButton"].waitToAppear().tap()
app.buttons["newRuleButton"].waitToAppear()
XCTAssertFalse(
app.buttons["ruleCard-Sleep"].waitForExistence(timeout: 2),
"Deleted rule's card should disappear"
)
XCTAssertTrue(app.buttons["ruleCard-Work Time"].exists)
}
func testUnblockActiveSoftRule() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
// The active rule surfaces in Blocked Apps; unblocking pauses it.
app.buttons["blockedTile-Work Time"].waitToAppear().tap()
app.sheets.buttons["Unblock"].waitToAppear().tap()
app.staticTexts["nothingBlockedLabel"].waitToAppear()
XCTAssertEqual(app.staticTexts["ruleStatus-Work Time"].waitToAppear().label, "Paused")
}
}
/// Hard block behavior seeded with an actively-blocking Hard Mode rule.
final class HardModeUITests: XCTestCase {
override func setUpWithError() throws {
continueAfterFailure = false
}
func testHardLockedRuleCannotBeEdited() throws {
let app = XCUIApplication.launchSevered(seedScenario: "hard-mode-active")
app.buttons["ruleCard-Locked In"].waitToAppear().tap()
// The lock notice replaces Edit Rule entirely.
app.element("hardModeLockedNotice").waitToAppear()
XCTAssertFalse(app.buttons["editRuleButton"].exists)
XCTAssertTrue(app.element("detailRow-Unblocks allowed").label.contains("No"))
}
func testHardLockedRuleCannotBeUnblocked() throws {
let app = XCUIApplication.launchSevered(seedScenario: "hard-mode-active")
app.buttons["blockedTile-Locked In"].waitToAppear().tap()
// No unblock dialog just the refusal alert.
let alert = app.alerts["Hard Mode is on"].waitToAppear()
XCTAssertTrue(alert.staticTexts["This block can't be lifted until it ends."].exists)
alert.buttons["OK"].tap()
// Still blocked.
XCTAssertTrue(app.buttons["blockedTile-Locked In"].exists)
XCTAssertFalse(app.staticTexts["nothingBlockedLabel"].exists)
}
func testSoftRuleUnblockOfferedButHardRuleRefused() throws {
let app = XCUIApplication.launchSevered(seedScenario: "standard")
app.buttons["blockedTile-Work Time"].waitToAppear().tap()
// Soft rule: the confirmation dialog appears instead of the refusal alert.
app.sheets.buttons["Unblock"].waitToAppear()
XCTAssertFalse(app.alerts["Hard Mode is on"].exists)
}
}