| @@ -33,8 +33,10 @@ final class LiveSmokeUITests: XCTestCase { |
| 33 | 33 | app = XCUIApplication() |
| 34 | 34 | // xcodebuild runs tests on simulator clones, so the base |
| 35 | 35 | // device's appearance never applies; force it per run. |
| 36 | | if ProcessInfo.processInfo.environment["GITBAY_UITEST_DARK"] == "1" { |
| 37 | | app.launchArguments.append("-gb-dark") |
| 36 | switch ProcessInfo.processInfo.environment["GITBAY_UITEST_DARK"] { |
| 37 | case "1": app.launchArguments.append("-gb-dark") |
| 38 | case "0": app.launchArguments.append("-gb-light") |
| 39 | default: break // follow the device, as a real launch does |
| 38 | 40 | } |
| 39 | 41 | app.launch() |
| 40 | 42 | if name.contains("testRepoManagementFlows") { |
| @@ -44,6 +46,9 @@ final class LiveSmokeUITests: XCTestCase { |
| 44 | 46 | ensureFixtureIssue() |
| 45 | 47 | try requireFixtureBranch() |
| 46 | 48 | } |
| 49 | if name.contains("testBlameAndEditFlows") { |
| 50 | try requireFixtureRepo() |
| 51 | } |
| 47 | 52 | } |
| 48 | 53 | |
| 49 | 54 | override func tearDownWithError() throws { |
| @@ -273,6 +278,21 @@ final class LiveSmokeUITests: XCTestCase { |
| 273 | 278 | keys.tap() |
| 274 | 279 | } |
| 275 | 280 | |
| 281 | /// The edit flow saves a change to a real file. Seeding a |
| 282 | /// repository's first commit is a push, which no test can make, so |
| 283 | /// this only checks — and says why, rather than failing later at |
| 284 | /// "cmc/ui-smoke-edit not in the repo list". |
| 285 | private func requireFixtureRepo() throws { |
| 286 | guard let data = readCommand(["repo", "show", "cmc/ui-smoke-edit"]), |
| 287 | let envelope = try? JSONSerialization.jsonObject(with: data) as? [String: Any] |
| 288 | else { return } // no token, or the instance is unreachable |
| 289 | try XCTSkipUnless(envelope["data"] != nil, """ |
| 290 | cmc/ui-smoke-edit is missing. The edit flow saves a change to \ |
| 291 | notes.txt in it; recreate the repo with that file (see its \ |
| 292 | README) and run again. |
| 293 | """) |
| 294 | } |
| 295 | |
| 276 | 296 | /// Scroll a list until an element is in the hierarchy. Offscreen |
| 277 | 297 | /// rows do not exist to XCUITest, so waitForExistence alone fails on |
| 278 | 298 | /// anything below the fold. |
| @@ -291,7 +311,12 @@ final class LiveSmokeUITests: XCTestCase { |
| 291 | 311 | /// swallowed silently, so this taps until the tab reports selected. |
| 292 | 312 | func selectTab(_ name: String, |
| 293 | 313 | file: StaticString = #filePath, line: UInt = #line) { |
| 294 | | let tab = app.tabBars.buttons[name].firstMatch |
| 314 | // iPadOS renders a TabView as a sidebar or top bar rather than a |
| 315 | // bottom tab bar, so the control is not under app.tabBars there. |
| 316 | var tab = app.tabBars.buttons[name].firstMatch |
| 317 | if !tab.waitForExistence(timeout: 5) { |
| 318 | tab = app.buttons[name].firstMatch |
| 319 | } |
| 295 | 320 | XCTAssertTrue(tab.waitForExistence(timeout: 15), |
| 296 | 321 | "\(name) tab missing", file: file, line: line) |
| 297 | 322 | // The app is interactive once its own chrome is hittable. |
| @@ -994,3 +1019,36 @@ extension LiveSmokeUITests { |
| 994 | 1019 | .waitForExistence(timeout: 20), "build detail shows no timing") |
| 995 | 1020 | } |
| 996 | 1021 | } |
| 1022 | |
| 1023 | extension LiveSmokeUITests { |
| 1024 | |
| 1025 | /// Store screenshots on iPad. It navigates by tab and through |
| 1026 | /// Explore rather than reusing the iPhone checkpoint: that one drives |
| 1027 | /// the repo list's `.searchable`, which iPadOS lays out differently, |
| 1028 | /// and chasing those differences is the iPad pass this release |
| 1029 | /// deferred. Tabs and a list row work on both. |
| 1030 | func testIPadScreenshotCheckpoint() throws { |
| 1031 | signInIfNeeded() |
| 1032 | snap("dashboard") |
| 1033 | |
| 1034 | selectTab("Explore") |
| 1035 | snap("explore") |
| 1036 | |
| 1037 | // First public repo in the list — no search field involved. |
| 1038 | let firstRepo = app.cells.firstMatch |
| 1039 | XCTAssertTrue(firstRepo.waitForExistence(timeout: 20), "explore listed nothing") |
| 1040 | firstRepo.tap() |
| 1041 | XCTAssertTrue(app.staticTexts["Files"].firstMatch.waitForExistence(timeout: 20), |
| 1042 | "repo screen did not open") |
| 1043 | snap("repo") |
| 1044 | |
| 1045 | selectTab("Feed") |
| 1046 | snap("feed") |
| 1047 | |
| 1048 | selectTab("My Profile") |
| 1049 | XCTAssertTrue(app.staticTexts |
| 1050 | .containing(NSPredicate(format: "label BEGINSWITH 'Repositories'")).firstMatch |
| 1051 | .waitForExistence(timeout: 20), "profile did not load") |
| 1052 | snap("profile") |
| 1053 | } |
| 1054 | } |