import XCTest /// Board list → lanes → cards → card detail, then an edit that has to reach disk — the one /// end-to-end walk of the mobile MVP's navigation stack (`BoardRoute`'s three destinations). final class BoardsNavigationUITests: XCTestCase { @MainActor func testBoardsNavigationAndTitleEdit() throws { let (app, root) = XCUIApplication.launchedWithFixtureBoard() // Boards is the app's root screen (`BoardsTabView`), so no navigation is needed to reach // it. The row's label is the flattened `BoardSummaryRow` — title plus the "N lanes · N // cards · modified" subtitle `BoardIndexStore`'s first scan fills in. let boardRow = app.element(labelContaining: RichBoard.title) XCTAssertTrue( boardRow.waitForExistence(timeout: XCUIApplication.uiTimeout), "the \"\(RichBoard.title)\" row never appeared — check the fixture copy or the first scan" ) boardRow.tap() let laneRow = app.element(labelContaining: RichBoard.firstLane) XCTAssertTrue( laneRow.waitForExistence(timeout: XCUIApplication.uiTimeout), "the \"\(RichBoard.firstLane)\" lane row never appeared" ) laneRow.tap() let cardRow = app.element(labelContaining: RichBoard.firstLaneFirstCard) XCTAssertTrue( cardRow.waitForExistence(timeout: XCUIApplication.uiTimeout), "the \"\(RichBoard.firstLaneFirstCard)\" card row never appeared" ) cardRow.tap() // CardDetailScreen: the only `textField` on this screen is the title // (`Section("Title")`) — the body is a `TextEditor`, which is a `textView`. let titleField = app.textFields.firstMatch XCTAssertTrue( titleField.waitForExistence(timeout: XCUIApplication.uiTimeout), "CardDetailScreen's title field never appeared" ) XCTAssertEqual( titleField.value as? String, RichBoard.firstLaneFirstCard, "the title field did not seed with the fixture card's own title" ) let sentinel = "Retitled by UI test" titleField.tap() // Select-all via a hardware-keyboard shortcut (iOS answers ⌘A the same as macOS when a // keyboard is attached, which the Simulator always presents one as) rather than a // backspace-per-character workaround, whose delete count only works if the tap happened // to land the cursor at the end of the existing text. titleField.typeKey("a", modifierFlags: .command) titleField.typeText(sentinel) // Commit the way the screen commits (`CardDetailScreen.body`'s `.confirmationAction`): // the Done button folds `commitAll()` in and drops focus, without navigating back. app.navigationBars.buttons["Done"].tap() XCTAssertTrue( waitForFile(under: root, containing: sentinel), "the retitled card never landed on disk under \(root.path)" ) } }