| @@ -10,8 +10,10 @@ import XCTest |
| 10 | /// TEST_RUNNER_GITBAY_UITEST_LIVE=1 xcodebuild test \ |
10 | /// TEST_RUNNER_GITBAY_UITEST_LIVE=1 xcodebuild test \ |
| 11 | /// -only-testing:gitbayUITests ... |
11 | /// -only-testing:gitbayUITests ... |
| 12 | /// |
12 | /// |
| 13 | /// They assume the simulator is already signed in as an account that can |
13 | /// They run as the `ios-smoke` account, a krz admin that owns the |
| 14 | /// write to krz/gitbay-ios, and that branch `ui-smoke` exists. |
14 | /// scratch and fixture repositories (smoke-account.sh provisions it), and |
| |
15 | /// assume branch `ui-smoke` exists on krz/gitbay-ios. A signed-out device |
| |
16 | /// is signed in at setUp from TEST_RUNNER_GITBAY_UITEST_TOKEN. |
| 15 | final class LiveSmokeUITests: XCTestCase { |
17 | final class LiveSmokeUITests: XCTestCase { |
| 16 | |
18 | |
| 17 | private var app: XCUIApplication! |
19 | private var app: XCUIApplication! |
| @@ -22,7 +24,15 @@ final class LiveSmokeUITests: XCTestCase { |
| 22 | /// because the name is taken, which reads like a UI bug and is not. |
24 | /// because the name is taken, which reads like a UI bug and is not. |
| 23 | /// Removed before and after, so neither a crashed run nor this one |
25 | /// Removed before and after, so neither a crashed run nor this one |
| 24 | /// can strand it. |
26 | /// can strand it. |
| 25 | private static let scratchRepo = "cmc/ui-smoke" |
27 | private static let scratchRepo = "\(smokeUser)/ui-smoke" |
| |
28 | |
| |
29 | /// The account the suite runs as. smoke-account.sh creates it with |
| |
30 | /// the profile description, PGP key and fixture repository the |
| |
31 | /// assertions below read back. |
| |
32 | static let smokeUser = "ios-smoke" |
| |
33 | static let smokeProfileDescription = "Live UI smoke account for gitbay-ios" |
| |
34 | static let smokePGPEmail = "ios-smoke+pgp@gitbay.org" |
| |
35 | static let editRepo = "\(smokeUser)/ui-smoke-edit" |
| 26 | |
36 | |
| 27 | override func setUpWithError() throws { |
37 | override func setUpWithError() throws { |
| 28 | try XCTSkipUnless( |
38 | try XCTSkipUnless( |
| @@ -408,15 +418,15 @@ final class LiveSmokeUITests: XCTestCase { |
| 408 | /// The edit flow saves a change to a real file. Seeding a |
418 | /// The edit flow saves a change to a real file. Seeding a |
| 409 | /// repository's first commit is a push, which no test can make, so |
419 | /// repository's first commit is a push, which no test can make, so |
| 410 | /// this only checks — and says why, rather than failing later at |
420 | /// this only checks — and says why, rather than failing later at |
| 411 | /// "cmc/ui-smoke-edit not in the repo list". |
421 | /// "ios-smoke/ui-smoke-edit not in the repo list". |
| 412 | private func requireFixtureRepo() throws { |
422 | private func requireFixtureRepo() throws { |
| 413 | guard let data = readCommand(["repo", "show", "cmc/ui-smoke-edit"]), |
423 | guard let data = readCommand(["repo", "show", Self.editRepo]), |
| 414 | let envelope = try? JSONSerialization.jsonObject(with: data) as? [String: Any] |
424 | let envelope = try? JSONSerialization.jsonObject(with: data) as? [String: Any] |
| 415 | else { return } // no token, or the instance is unreachable |
425 | else { return } // no token, or the instance is unreachable |
| 416 | try XCTSkipUnless(envelope["data"] != nil, """ |
426 | try XCTSkipUnless(envelope["data"] != nil, """ |
| 417 | cmc/ui-smoke-edit is missing. The edit flow saves a change to \ |
427 | \(Self.editRepo) is missing. The edit flow saves a change to \ |
| 418 | notes.txt in it; recreate the repo with that file (see its \ |
428 | notes.txt in it; gitbayUITests/smoke-account.sh seeds it (or \ |
| 419 | README) and run again. |
429 | push it by hand, see that script) and run again. |
| 420 | """) |
430 | """) |
| 421 | } |
431 | } |
| 422 | |
432 | |
| @@ -533,11 +543,11 @@ extension LiveSmokeUITests { |
| 533 | let createSearch = app.searchFields.firstMatch |
543 | let createSearch = app.searchFields.firstMatch |
| 534 | XCTAssertTrue(createSearch.waitForExistence(timeout: 10)) |
544 | XCTAssertTrue(createSearch.waitForExistence(timeout: 10)) |
| 535 | focusAndType(createSearch, "ui-smoke") |
545 | focusAndType(createSearch, "ui-smoke") |
| 536 | XCTAssertTrue(app.staticTexts["cmc/ui-smoke"].firstMatch |
546 | XCTAssertTrue(app.staticTexts[Self.scratchRepo].firstMatch |
| 537 | .waitForExistence(timeout: 15), "created repo not in the list") |
547 | .waitForExistence(timeout: 15), "created repo not in the list") |
| 538 | |
548 | |
| 539 | // --- access: grant a user on the scratch repo, then revoke --- |
549 | // --- access: grant a user on the scratch repo, then revoke --- |
| 540 | app.staticTexts["cmc/ui-smoke"].firstMatch.tap() |
550 | app.staticTexts[Self.scratchRepo].firstMatch.tap() |
| 541 | XCTAssertTrue(app.staticTexts["Files"].firstMatch.waitForExistence(timeout: 10)) |
551 | XCTAssertTrue(app.staticTexts["Files"].firstMatch.waitForExistence(timeout: 10)) |
| 542 | app.staticTexts["Settings"].firstMatch.tap() |
552 | app.staticTexts["Settings"].firstMatch.tap() |
| 543 | let grantUser = app.descendants(matching: .any) |
553 | let grantUser = app.descendants(matching: .any) |
| @@ -582,7 +592,7 @@ extension LiveSmokeUITests { |
| 582 | |
592 | |
| 583 | // --- rename the scratch repo there and back --- |
593 | // --- rename the scratch repo there and back --- |
| 584 | // Each rename pushes the renamed repository; the round-trip ends |
594 | // Each rename pushes the renamed repository; the round-trip ends |
| 585 | // on cmc/ui-smoke again, several screens deep. |
595 | // on the scratch repo again, several screens deep. |
| 586 | var firstRename = true |
596 | var firstRename = true |
| 587 | let rename: (String, String) -> Void = { newName, expectedPath in |
597 | let rename: (String, String) -> Void = { newName, expectedPath in |
| 588 | if firstRename { |
598 | if firstRename { |
| @@ -616,8 +626,8 @@ extension LiveSmokeUITests { |
| 616 | XCTAssertTrue(self.app.staticTexts[expectedPath].firstMatch.waitForExistence(timeout: 15), |
626 | XCTAssertTrue(self.app.staticTexts[expectedPath].firstMatch.waitForExistence(timeout: 15), |
| 617 | "renamed repo did not open as \(expectedPath)") |
627 | "renamed repo did not open as \(expectedPath)") |
| 618 | } |
628 | } |
| 619 | rename("ui-smoke-2", "cmc/ui-smoke-2") |
629 | rename("ui-smoke-2", Self.scratchRepo + "-2") |
| 620 | rename("ui-smoke", "cmc/ui-smoke") |
630 | rename("ui-smoke", Self.scratchRepo) |
| 621 | |
631 | |
| 622 | // --- pin / unpin round-trip on krz/gitbay-ios --- |
632 | // --- pin / unpin round-trip on krz/gitbay-ios --- |
| 623 | // Start the list fresh; the rename left the stack several deep. |
633 | // Start the list fresh; the rename left the stack several deep. |
| @@ -939,7 +949,7 @@ extension LiveSmokeUITests { |
| 939 | |
949 | |
| 940 | // The PGP key's UID email, below the keys on a List that does not |
950 | // The PGP key's UID email, below the keys on a List that does not |
| 941 | // build rows it has not shown. |
951 | // build rows it has not shown. |
| 942 | XCTAssertTrue(scrollTo(app.staticTexts["hello@cleberg.net"].firstMatch), |
952 | XCTAssertTrue(scrollTo(app.staticTexts[Self.smokePGPEmail].firstMatch), |
| 943 | "PGP key UID missing") |
953 | "PGP key UID missing") |
| 944 | |
954 | |
| 945 | // Activity mail flips off and back on; the value comes back from |
955 | // Activity mail flips off and back on; the value comes back from |
| @@ -1010,7 +1020,7 @@ extension LiveSmokeUITests { |
| 1010 | orgRow.tap() |
1020 | orgRow.tap() |
| 1011 | |
1021 | |
| 1012 | // Members render with roles. |
1022 | // Members render with roles. |
| 1013 | XCTAssertTrue(app.staticTexts["cmc"].firstMatch |
1023 | XCTAssertTrue(app.staticTexts[Self.smokeUser].firstMatch |
| 1014 | .waitForExistence(timeout: 15), "org members missing") |
1024 | .waitForExistence(timeout: 15), "org members missing") |
| 1015 | |
1025 | |
| 1016 | // Create a team. |
1026 | // Create a team. |
| @@ -1176,7 +1186,7 @@ extension LiveSmokeUITests { |
| 1176 | // previous query, and clearing it from the harness is unreliable. |
1186 | // previous query, and clearing it from the harness is unreliable. |
| 1177 | app.terminate() |
1187 | app.terminate() |
| 1178 | app.launch() |
1188 | app.launch() |
| 1179 | openRepo("cmc/ui-smoke-edit") |
1189 | openRepo(Self.editRepo) |
| 1180 | app.staticTexts["Files"].firstMatch.tap() |
1190 | app.staticTexts["Files"].firstMatch.tap() |
| 1181 | let notes = app.staticTexts["notes.txt"].firstMatch |
1191 | let notes = app.staticTexts["notes.txt"].firstMatch |
| 1182 | XCTAssertTrue(notes.waitForExistence(timeout: 15), "notes.txt not in the tree") |
1192 | XCTAssertTrue(notes.waitForExistence(timeout: 15), "notes.txt not in the tree") |
| @@ -1242,7 +1252,7 @@ extension LiveSmokeUITests { |
| 1242 | |
1252 | |
| 1243 | // --- a profile is a profile: description, links, orgs, graph, repos --- |
1253 | // --- a profile is a profile: description, links, orgs, graph, repos --- |
| 1244 | XCTAssertTrue(app.staticTexts |
1254 | XCTAssertTrue(app.staticTexts |
| 1245 | .containing(NSPredicate(format: "label CONTAINS 'Self-Hosting'")).firstMatch |
1255 | .containing(NSPredicate(format: "label CONTAINS %@", Self.smokeProfileDescription)).firstMatch |
| 1246 | .waitForExistence(timeout: 20), "profile description missing") |
1256 | .waitForExistence(timeout: 20), "profile description missing") |
| 1247 | XCTAssertTrue(header("Organizations").exists, |
1257 | XCTAssertTrue(header("Organizations").exists, |
| 1248 | "org memberships missing") |
1258 | "org memberships missing") |
| @@ -1467,8 +1477,10 @@ extension LiveSmokeUITests { |
| 1467 | snap("feed") |
1477 | snap("feed") |
| 1468 | |
1478 | |
| 1469 | selectTab("My Profile") |
1479 | selectTab("My Profile") |
| 1470 | XCTAssertTrue(app.staticTexts |
1480 | // Any signed-in account can edit its own profile, so this holds |
| 1471 | .containing(NSPredicate(format: "label CONTAINS 'Self-Hosting'")).firstMatch |
1481 | // for store screenshots taken as a real user too. |
| |
1482 | XCTAssertTrue(app.descendants(matching: .any) |
| |
1483 | .matching(identifier: "profile-edit-button").firstMatch |
| 1472 | .waitForExistence(timeout: 20), "profile did not load") |
1484 | .waitForExistence(timeout: 20), "profile did not load") |
| 1473 | snap("profile") |
1485 | snap("profile") |
| 1474 | } |
1486 | } |
| @@ -1631,7 +1643,7 @@ extension LiveSmokeUITests { |
| 1631 | let orgRow = app.staticTexts[Self.smokeOrg].firstMatch |
1643 | let orgRow = app.staticTexts[Self.smokeOrg].firstMatch |
| 1632 | XCTAssertTrue(orgRow.waitForExistence(timeout: 15), "org list missing") |
1644 | XCTAssertTrue(orgRow.waitForExistence(timeout: 15), "org list missing") |
| 1633 | orgRow.tap() |
1645 | orgRow.tap() |
| 1634 | XCTAssertTrue(app.staticTexts["cmc"].firstMatch.waitForExistence(timeout: 15), |
1646 | XCTAssertTrue(app.staticTexts[Self.smokeUser].firstMatch.waitForExistence(timeout: 15), |
| 1635 | "org members missing") |
1647 | "org members missing") |
| 1636 | |
1648 | |
| 1637 | // --- labels --- |
1649 | // --- labels --- |