import XCTest /// Where a board is stored, and the one operation that changes it. /// /// Both tests drive the real `BoardIndexStore.relocateBoard(at:to:)` over two real directories the /// test owns — the DEBUG stand-in cloud root and the DEBUG device root — so the move that lands is a /// coordinated move of an actual `.kanban` package, not a model edit. The `setUbiquitous` path a /// shipped build takes cannot be exercised without an iCloud account; what these cover is everything /// on either side of it. final class BoardLocationUITests: XCTestCase { @MainActor func testMoveBoardOutOfICloudAndBack() throws { let (app, cloudRoot, deviceRoot) = XCUIApplication.launchedWithFixtureBoardInCloud() let inCloud = cloudRoot.appendingPathComponent(RichBoard.packageName, isDirectory: true) let onDevice = deviceRoot.appendingPathComponent(RichBoard.packageName, isDirectory: true) // Out of iCloud. app.openBoardSettings(for: RichBoard.title) // `LabeledContent` publishes label and value as one flattened node — "Stored In, iCloud" — // which is the sheet's whole claim about where the board is, in one assertion. XCTAssertTrue( app.staticTexts["Stored In, iCloud"].waitForExistence(timeout: XCUIApplication.uiTimeout), "the sheet did not report the board as stored in iCloud" ) app.buttons["Move to Local"].tap() app.confirmMove() XCTAssertTrue( app.navigationBars["Board Settings"].waitForNonExistence(timeout: XCUIApplication.uiTimeout), "the sheet stayed up after a move that should have succeeded" ) XCTAssertTrue( waitForDirectory(at: onDevice), "the package never arrived under the device root at \(onDevice.path)" ) XCTAssertTrue( waitForDirectory(at: inCloud, toExist: false), "the package was still under the cloud root at \(inCloud.path)" ) XCTAssertTrue( app.boardRow(RichBoard.title, alsoContaining: "Local") .waitForExistence(timeout: XCUIApplication.uiTimeout), "the row never picked up the Local marker" ) // And back. app.openBoardSettings(for: RichBoard.title) XCTAssertTrue( app.staticTexts["Stored In, Local"].waitForExistence(timeout: XCUIApplication.uiTimeout), "the sheet did not report the moved board as stored locally" ) let moveToCloud = app.buttons["Move to iCloud"] XCTAssertTrue( moveToCloud.waitForExistence(timeout: XCUIApplication.uiTimeout), "the sheet did not offer the return trip" ) moveToCloud.tap() app.confirmMove() XCTAssertTrue( waitForDirectory(at: inCloud), "the package never came back under the cloud root at \(inCloud.path)" ) XCTAssertTrue( waitForDirectory(at: onDevice, toExist: false), "the package was still under the device root at \(onDevice.path)" ) } /// The softened wall (2026-08-08): no iCloud is a notice above a working list, not a screen /// instead of one. @MainActor func testLocalBoardWorksWithoutICloud() throws { let (app, _) = XCUIApplication.launchedWithFixtureBoardOnDevice() XCTAssertTrue( app.element(labelContaining: "iCloud Unavailable").waitForExistence(timeout: XCUIApplication.uiTimeout), "the iCloud notice never appeared" ) let boardRow = app.boardRow(RichBoard.title, alsoContaining: "Local") XCTAssertTrue( boardRow.waitForExistence(timeout: XCUIApplication.uiTimeout), "the device-home board never listed — the old wall would have replaced the list here" ) // It is a whole board, not a listing: it opens and navigates like any other. boardRow.tap() let laneRow = app.element(labelContaining: RichBoard.firstLane) XCTAssertTrue( laneRow.waitForExistence(timeout: XCUIApplication.uiTimeout), "the local board's lanes never appeared" ) app.navigationBars.buttons.element(boundBy: 0).tap() // The one thing it cannot do: go somewhere that does not exist. app.openBoardSettings(for: RichBoard.title) XCTAssertTrue( app.staticTexts["Stored In, Local"].waitForExistence(timeout: XCUIApplication.uiTimeout), "the sheet did not report the board as stored locally" ) let moveToCloud = app.buttons["Move to iCloud"] XCTAssertTrue( moveToCloud.waitForExistence(timeout: XCUIApplication.uiTimeout), "the sheet never offered a move to iCloud" ) XCTAssertFalse( moveToCloud.isEnabled, "moving to iCloud was offered as available with no iCloud to move to" ) } }