import XCTest /// Taps through the app against live knotless.dev, as the account the run /// names. Needs the network, so it is run by hand: see ios/README.md. final class FlowTests: XCTestCase { private var app: XCUIApplication! private let shots = ProcessInfo.processInfo.environment["KNOTLESS_SHOTS"] override func setUp() { continueAfterFailure = false app = XCUIApplication() for key in ["KNOTLESS_TOKENS"] { app.launchEnvironment[key] = ProcessInfo.processInfo.environment[key] } app.launch() } private func shot(_ name: String) { guard let shots else { return } try? XCUIScreen.main.screenshot().pngRepresentation.write(to: URL(fileURLWithPath: "\(shots)/\(name).png")) } private func tab(_ name: String) { app.tabBars.buttons[name].tap() } private func see(_ text: String, timeout: TimeInterval = 30) { let found = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", text)).firstMatch XCTAssertTrue(found.waitForExistence(timeout: timeout), "\(text) never showed") } private func notFailed() { XCTAssertFalse(app.staticTexts["This didn't load"].waitForExistence(timeout: 2), "A screen failed to load") } func testAThreadsHeaderOpensOnlyFromItsLinks() { app.terminate() let did = "did:plc:bcgltzqazw5tb6k2g3ttenbj" app.launchEnvironment["KNOTLESS_ROUTE"] = "thread|\(did)|chadtmiller.com|stack-test|at://\(did)/dev.knotless.pull/3mzkcgac7w4is" app.launch() let title = app.staticTexts["Pull c1"].firstMatch XCTAssertTrue(title.waitForExistence(timeout: 30)) app.staticTexts["Fails"].tap() sleep(2) XCTAssertTrue(title.isHittable, "Touching the description left the pull") app.buttons["thread-author"].tap() see("followers") } func testInboxOpensAPullAndItsFiles() { tab("Inbox") let row = app.cells.firstMatch XCTAssertTrue(row.waitForExistence(timeout: 30)) shot("flow-inbox") row.tap() let changes = app.staticTexts.containing(NSPredicate(format: "label CONTAINS[c] %@", "changed")).firstMatch for _ in 0..<6 where !changes.waitForExistence(timeout: 8) { app.swipeUp() } XCTAssertTrue(changes.exists, "No changes row on the pull") notFailed() shot("flow-private-pull") changes.tap() see("Files changed") notFailed() sleep(4) shot("flow-files") app.swipeUp() sleep(2) shot("flow-files-scrolled") notFailed() } /// A row scrolled past before its diff comes has its read cancelled, which /// isn't a read that failed: coming back to it reads it again. func testFlickingPastAPullsFilesLeavesThemToLoad() { app.terminate() let did = "did:plc:bcgltzqazw5tb6k2g3ttenbj" app.launchEnvironment["KNOTLESS_ROUTE"] = "files|\(did)|chadtmiller.com|knotless|at://\(did)/dev.knotless.pull/3myzh6t32uark" app.launch() see("Files changed") let failed = app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", "diff didn't load")).firstMatch for _ in 0..<8 { app.swipeUp(velocity: .fast) } for _ in 0..<8 { app.swipeDown(velocity: .fast) } sleep(4) shot("flow-files-flicked") XCTAssertFalse(failed.exists, "A file scrolled past stayed unread") for _ in 0..<6 { app.swipeUp() XCTAssertTrue(app.activityIndicators.firstMatch.waitForNonExistence(timeout: 20), "A file scrolled past never loaded") XCTAssertFalse(failed.exists, "A file scrolled past stayed unread") } } func testRepoToCodeAndAFile() { tab("Explore") let row = app.cells.firstMatch XCTAssertTrue(row.waitForExistence(timeout: 30)) tab("Home") see("Your repositories") app.swipeUp() app.cells.element(boundBy: 6).tap() see("star") notFailed() app.staticTexts["Code"].tap() XCTAssertTrue(app.cells.firstMatch.waitForExistence(timeout: 30)) notFailed() shot("flow-tree") } func testKnotlessPullsOpenClosedAndDiffScrolls() { tab("Profile") see("followers") app.swipeUp() tab("Explore") let merged = app.cells.containing(NSPredicate(format: "label CONTAINS[c] %@", "chadtmiller.com/knotless")).firstMatch XCTAssertTrue(merged.waitForExistence(timeout: 30)) merged.tap() notFailed() shot("flow-explore-target") } }