import XCTest /// Settings' only section — `SettingsScreen`'s `IndieAbout` block: version/build (which /// doubles as the changelog link), the License document link, and the copyright line. final class SettingsAboutUITests: XCTestCase { @MainActor func testSettingsAboutSection() throws { let (app, _) = XCUIApplication.launchedWithFixtureBoard() app.navigationBars.buttons["Settings"].tap() // The About section is the only section in the form — a speculative swipe settles the // list near the bottom before anything here is queried, harmless if the content already // fit on screen. app.swipeUp() // `IndieAbout`'s version line is three sibling `Text` views (prefix, the tappable // "1.0-" link, suffix) — none of them wrapped in a control that would flatten them // into one accessibility element, so the link is addressable on its own by the substring // only it carries. `MARKETING_VERSION` for KanbanMobile is "1.0" (project.yml). let versionLink = app.staticTexts.matching(NSPredicate(format: "label CONTAINS %@", "1.0")).firstMatch XCTAssertTrue( versionLink.waitForExistence(timeout: XCUIApplication.uiTimeout), "no version label containing \"1.0\" appeared in Settings" ) let licenseLink = app.staticTexts["License"] XCTAssertTrue(licenseLink.waitForExistence(timeout: XCUIApplication.uiTimeout), "the License link never appeared") let copyright = app.element(labelContaining: "2026 rzen") XCTAssertTrue(copyright.waitForExistence(timeout: XCUIApplication.uiTimeout), "the copyright text never appeared") // Tap the version link — opens the changelog (`IndieAbout.versionLineView`'s // `.onTapGesture`, wired to `changelogDocument: .changelog()` in `SettingsScreen`). app.scrollUntilHittable(versionLink) versionLink.tap() let changelogText = app.element(labelContaining: "August 2026") XCTAssertTrue( changelogText.waitForExistence(timeout: XCUIApplication.uiTimeout), "the changelog sheet never showed \"August 2026\" (KanbanMobile/CHANGELOG.md)" ) // Scoped to the "Changelog" nav bar specifically — `SettingsScreen` is itself a sheet with // its own "Done" button, and that nav bar stays in the accessibility tree underneath this // one, so an unscoped `app.navigationBars.buttons["Done"]` would match both. app.navigationBars["Changelog"].buttons["Done"].tap() // Tap License — opens the bundled ISC license. app.scrollUntilHittable(licenseLink) licenseLink.tap() let licenseText = app.element(labelContaining: "ISC License") XCTAssertTrue( licenseText.waitForExistence(timeout: XCUIApplication.uiTimeout), "the license sheet never showed \"ISC License\" (LICENSE.md)" ) // Same scoping reason as the changelog Done tap above. app.navigationBars["License"].buttons["Done"].tap() } }