* Rename cmuxterm to cmux across entire codebase - Rename GitHub repos: manaflow-ai/cmuxterm -> manaflow-ai/cmux, manaflow-ai/homebrew-cmuxterm -> manaflow-ai/homebrew-cmux - Rename bundle IDs: com.cmuxterm.app -> com.cmux.app - Rename CLI: CLI/cmuxterm.swift -> CLI/cmux.swift - Rename homebrew submodule: homebrew-cmuxterm -> homebrew-cmux - Update all socket paths: /tmp/cmuxterm*.sock -> /tmp/cmux*.sock - Update all GitHub URLs, DMG names, Sparkle URLs - Update all source files, scripts, tests, docs, CI workflows * Bump version to 1.23.0
136 lines
5.9 KiB
Swift
136 lines
5.9 KiB
Swift
import XCTest
|
|
import Foundation
|
|
|
|
final class UpdatePillUITests: XCTestCase {
|
|
override func setUp() {
|
|
super.setUp()
|
|
continueAfterFailure = false
|
|
}
|
|
|
|
func testUpdatePillShowsForAvailableUpdate() {
|
|
let systemSettings = XCUIApplication(bundleIdentifier: "com.apple.systempreferences")
|
|
systemSettings.terminate()
|
|
let app = XCUIApplication()
|
|
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
|
|
app.launchEnvironment["CMUX_UI_TEST_UPDATE_STATE"] = "available"
|
|
app.launchEnvironment["CMUX_UI_TEST_UPDATE_VERSION"] = "9.9.9"
|
|
app.launch()
|
|
app.activate()
|
|
|
|
let pill = app.descendants(matching: .any)["UpdatePill"]
|
|
XCTAssertTrue(pill.waitForExistence(timeout: 5.0))
|
|
XCTAssertTrue(waitForLabel(pill, label: "Update Available: 9.9.9", timeout: 5.0))
|
|
assertVisibleSize(pill)
|
|
attachScreenshot(name: "update-available")
|
|
attachScreenshot(name: "update-available-pill", screenshot: pill.screenshot())
|
|
}
|
|
|
|
func testUpdatePillShowsForNoUpdateThenDismisses() {
|
|
let systemSettings = XCUIApplication(bundleIdentifier: "com.apple.systempreferences")
|
|
systemSettings.terminate()
|
|
let timingPath = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("cmux-ui-test-timing-\(UUID().uuidString).json")
|
|
let app = XCUIApplication()
|
|
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
|
|
app.launchEnvironment["CMUX_UI_TEST_UPDATE_STATE"] = "notFound"
|
|
app.launchEnvironment["CMUX_UI_TEST_TIMING_PATH"] = timingPath.path
|
|
app.launch()
|
|
app.activate()
|
|
|
|
let pill = app.descendants(matching: .any)["UpdatePill"]
|
|
XCTAssertTrue(pill.waitForExistence(timeout: 5.0))
|
|
XCTAssertTrue(waitForLabel(pill, label: "No Updates Available", timeout: 5.0))
|
|
assertVisibleSize(pill)
|
|
attachScreenshot(name: "no-updates")
|
|
attachScreenshot(name: "no-updates-pill", screenshot: pill.screenshot())
|
|
|
|
let gone = XCTNSPredicateExpectation(
|
|
predicate: NSPredicate(format: "exists == false"),
|
|
object: pill
|
|
)
|
|
XCTAssertEqual(XCTWaiter().wait(for: [gone], timeout: 7.0), .completed)
|
|
|
|
let payload = loadTimingPayload(from: timingPath)
|
|
let shownAt = payload["noUpdateShownAt"] ?? 0
|
|
let hiddenAt = payload["noUpdateHiddenAt"] ?? 0
|
|
XCTAssertGreaterThan(shownAt, 0)
|
|
XCTAssertGreaterThan(hiddenAt, shownAt)
|
|
XCTAssertGreaterThanOrEqual(hiddenAt - shownAt, 4.8)
|
|
}
|
|
|
|
func testCheckForUpdatesUsesMockFeedWithUpdate() {
|
|
let systemSettings = XCUIApplication(bundleIdentifier: "com.apple.systempreferences")
|
|
systemSettings.terminate()
|
|
let app = launchAppWithMockFeed(mode: "available", version: "9.9.9")
|
|
|
|
let pill = app.descendants(matching: .any)["UpdatePill"]
|
|
XCTAssertTrue(pill.waitForExistence(timeout: 5.0))
|
|
XCTAssertTrue(waitForLabel(pill, label: "Update Available: 9.9.9", timeout: 5.0))
|
|
assertVisibleSize(pill)
|
|
attachScreenshot(name: "mock-update-available")
|
|
}
|
|
|
|
func testCheckForUpdatesUsesMockFeedWithNoUpdate() {
|
|
let systemSettings = XCUIApplication(bundleIdentifier: "com.apple.systempreferences")
|
|
systemSettings.terminate()
|
|
let timingPath = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("cmux-ui-test-timing-\(UUID().uuidString).json")
|
|
let app = launchAppWithMockFeed(mode: "none", version: "9.9.9", timingPath: timingPath)
|
|
|
|
let pill = app.descendants(matching: .any)["UpdatePill"]
|
|
XCTAssertTrue(pill.waitForExistence(timeout: 5.0))
|
|
XCTAssertTrue(waitForLabel(pill, label: "No Updates Available", timeout: 5.0))
|
|
assertVisibleSize(pill)
|
|
attachScreenshot(name: "mock-no-updates")
|
|
}
|
|
|
|
private func waitForLabel(_ element: XCUIElement, label: String, timeout: TimeInterval) -> Bool {
|
|
let predicate = NSPredicate(format: "label == %@", label)
|
|
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: element)
|
|
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
|
|
}
|
|
|
|
private func assertVisibleSize(_ element: XCUIElement, timeout: TimeInterval = 2.0) {
|
|
let deadline = Date().addingTimeInterval(timeout)
|
|
var size = element.frame.size
|
|
while Date() < deadline {
|
|
size = element.frame.size
|
|
if size.width > 20 && size.height > 10 {
|
|
return
|
|
}
|
|
RunLoop.current.run(until: Date().addingTimeInterval(0.05))
|
|
}
|
|
XCTFail("Expected UpdatePill to have visible size, got \(size)")
|
|
}
|
|
|
|
private func attachScreenshot(name: String, screenshot: XCUIScreenshot = XCUIScreen.main.screenshot()) {
|
|
let attachment = XCTAttachment(screenshot: screenshot)
|
|
attachment.name = name
|
|
attachment.lifetime = .keepAlways
|
|
add(attachment)
|
|
}
|
|
|
|
private func launchAppWithMockFeed(mode: String, version: String, timingPath: URL? = nil) -> XCUIApplication {
|
|
let app = XCUIApplication()
|
|
app.launchEnvironment["CMUX_UI_TEST_MODE"] = "1"
|
|
app.launchEnvironment["CMUX_UI_TEST_FEED_URL"] = "https://cmux.test/appcast.xml"
|
|
app.launchEnvironment["CMUX_UI_TEST_FEED_MODE"] = mode
|
|
app.launchEnvironment["CMUX_UI_TEST_UPDATE_VERSION"] = version
|
|
app.launchEnvironment["CMUX_UI_TEST_AUTO_ALLOW_PERMISSION"] = "1"
|
|
app.launchEnvironment["CMUX_UI_TEST_TRIGGER_UPDATE_CHECK"] = "1"
|
|
if let timingPath {
|
|
app.launchEnvironment["CMUX_UI_TEST_TIMING_PATH"] = timingPath.path
|
|
}
|
|
app.launch()
|
|
app.activate()
|
|
return app
|
|
}
|
|
|
|
private func loadTimingPayload(from url: URL) -> [String: Double] {
|
|
guard let data = try? Data(contentsOf: url),
|
|
let object = try? JSONSerialization.jsonObject(with: data) as? [String: Double] else {
|
|
return [:]
|
|
}
|
|
return object
|
|
}
|
|
}
|