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
This commit is contained in:
2026-06-12 15:44:25 -04:00
parent 0242e0a9d3
commit 4ee8f01d21
7 changed files with 86 additions and 36 deletions

View File

@@ -44,19 +44,38 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["newRuleButton"].waitToAppear().tap()
app.buttons["ruleKind-schedule"].waitToAppear().tap()
app.buttons["renameButton"].waitToAppear().tap()
let nameField = app.textFields.firstMatch.waitToAppear()
nameField.tap()
// Clear the prefilled name, then type the new one.
// The rule name is an inline text field at the top of the editor
// no separate edit/rename button.
XCTAssertFalse(app.buttons["renameButton"].exists)
let nameField = app.textFields["ruleNameField"].waitToAppear()
// Tap at the trailing edge so the cursor lands after the last character.
nameField.coordinate(withNormalizedOffset: CGVector(dx: 0.95, dy: 0.5)).tap()
let deletions = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 24)
nameField.typeText(deletions + "My Focus")
app.buttons["OK"].tap()
nameField.typeText(deletions + "My Focus\n")
XCTAssertEqual(app.staticTexts["ruleEditorTitle"].label, "My Focus")
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-My Focus"].waitToAppear()
}
func testDayTogglesFillRowAndHaveLargeTapTargets() throws {
let app = XCUIApplication.launchSevered()
app.buttons["newRuleButton"].waitToAppear().tap()
app.buttons["ruleKind-schedule"].waitToAppear().tap()
let first = app.buttons["dayToggle-1"].waitToAppear()
let last = app.buttons["dayToggle-7"].waitToAppear()
let span = last.frame.maxX - first.frame.minX
XCTAssertGreaterThan(
span, app.frame.width * 0.75,
"Day toggles should span the full row width, got \(span) of \(app.frame.width)"
)
XCTAssertGreaterThanOrEqual(
first.frame.height, 44,
"Day toggle tap target should be at least 44pt tall"
)
}
func testDayTogglesUpdateSummary() throws {
let app = XCUIApplication.launchSevered()
app.buttons["newRuleButton"].waitToAppear().tap()
@@ -90,6 +109,10 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["newRuleButton"].waitToAppear().tap()
app.buttons["ruleKind-schedule"].waitToAppear().tap()
// The toggle lives at the bottom of the form; scroll it clear of the
// commit bar before tapping.
app.staticTexts["ruleEditorTitle"].waitToAppear()
app.swipeUp()
app.switches["adultContentToggle"].waitToAppear().tap()
app.buttons["commitRuleButton"].waitToAppear().tap()

View File

@@ -47,6 +47,9 @@ final class RuleManagementUITests: XCTestCase {
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.
@@ -63,6 +66,9 @@ final class RuleManagementUITests: XCTestCase {
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()