216 lines
8.2 KiB
Swift
216 lines
8.2 KiB
Swift
import XCTest
|
|
import Foundation
|
|
|
|
final class CloseWorkspacesConfirmDialogUITests: XCTestCase {
|
|
private var socketPath = ""
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
continueAfterFailure = false
|
|
socketPath = "/tmp/cmux-ui-test-close-workspaces-\(UUID().uuidString).sock"
|
|
try? FileManager.default.removeItem(atPath: socketPath)
|
|
}
|
|
|
|
func testCommandPaletteCloseOtherWorkspacesShowsSingleSummaryDialog() {
|
|
let app = XCUIApplication()
|
|
app.launchEnvironment["CMUX_SOCKET_PATH"] = socketPath
|
|
app.launchEnvironment["CMUX_UI_TEST_FORCE_CONFIRM_CLOSE_WORKSPACE"] = "1"
|
|
app.launch()
|
|
XCTAssertTrue(
|
|
ensureForegroundAfterLaunch(app, timeout: 12.0),
|
|
"Expected app to launch for close-workspaces confirmation test. state=\(app.state.rawValue)"
|
|
)
|
|
XCTAssertTrue(waitForSocketPong(timeout: 12.0), "Expected control socket to respond at \(socketPath)")
|
|
|
|
XCTAssertEqual(socketCommand("new_workspace")?.prefix(2), "OK")
|
|
XCTAssertEqual(socketCommand("new_workspace")?.prefix(2), "OK")
|
|
XCTAssertTrue(
|
|
waitForWorkspaceCount(3, timeout: 5.0),
|
|
"Expected 3 workspaces before running the close-other-workspaces command. list=\(socketCommand("list_workspaces") ?? "<nil>")"
|
|
)
|
|
XCTAssertEqual(socketCommand("select_workspace 1"), "OK")
|
|
|
|
app.typeKey("p", modifierFlags: [.command, .shift])
|
|
|
|
let searchField = app.textFields["CommandPaletteSearchField"]
|
|
XCTAssertTrue(searchField.waitForExistence(timeout: 5.0), "Expected command palette search field")
|
|
searchField.click()
|
|
searchField.typeText("Close Other Workspaces")
|
|
|
|
let resultButton = app.buttons["Close Other Workspaces"].firstMatch
|
|
if resultButton.waitForExistence(timeout: 5.0) {
|
|
resultButton.click()
|
|
} else {
|
|
app.typeKey(.return, modifierFlags: [])
|
|
}
|
|
|
|
XCTAssertTrue(
|
|
waitForCloseWorkspacesAlert(app: app, timeout: 5.0),
|
|
"Expected a single aggregated close-workspaces alert"
|
|
)
|
|
|
|
clickCancelOnCloseWorkspacesAlert(app: app)
|
|
|
|
XCTAssertFalse(
|
|
isCloseWorkspacesAlertPresent(app: app),
|
|
"Expected aggregated close-workspaces alert to dismiss after clicking Cancel"
|
|
)
|
|
XCTAssertTrue(
|
|
waitForWorkspaceCount(3, timeout: 5.0),
|
|
"Expected all workspaces to remain after cancelling multi-close. list=\(socketCommand("list_workspaces") ?? "<nil>")"
|
|
)
|
|
}
|
|
|
|
func testCmdShiftWUsesSidebarMultiSelectionSummaryDialog() {
|
|
let app = XCUIApplication()
|
|
app.launchEnvironment["CMUX_SOCKET_PATH"] = socketPath
|
|
app.launchEnvironment["CMUX_UI_TEST_FORCE_CONFIRM_CLOSE_WORKSPACE"] = "1"
|
|
app.launchEnvironment["CMUX_UI_TEST_SIDEBAR_SELECTED_WORKSPACE_INDICES"] = "0,1"
|
|
app.launch()
|
|
XCTAssertTrue(
|
|
ensureForegroundAfterLaunch(app, timeout: 12.0),
|
|
"Expected app to launch for close-workspaces shortcut test. state=\(app.state.rawValue)"
|
|
)
|
|
XCTAssertTrue(waitForSocketPong(timeout: 12.0), "Expected control socket to respond at \(socketPath)")
|
|
|
|
XCTAssertEqual(socketCommand("new_workspace")?.prefix(2), "OK")
|
|
XCTAssertTrue(
|
|
waitForWorkspaceCount(2, timeout: 5.0),
|
|
"Expected 2 workspaces before running Cmd+Shift+W. list=\(socketCommand("list_workspaces") ?? "<nil>")"
|
|
)
|
|
|
|
app.typeKey("w", modifierFlags: [.command, .shift])
|
|
|
|
XCTAssertTrue(
|
|
waitForCloseWorkspacesAlert(app: app, timeout: 5.0),
|
|
"Expected Cmd+Shift+W to use the aggregated close-workspaces alert for sidebar multi-selection"
|
|
)
|
|
|
|
clickCancelOnCloseWorkspacesAlert(app: app)
|
|
|
|
XCTAssertFalse(
|
|
isCloseWorkspacesAlertPresent(app: app),
|
|
"Expected aggregated close-workspaces alert to dismiss after clicking Cancel"
|
|
)
|
|
XCTAssertTrue(
|
|
waitForWorkspaceCount(2, timeout: 5.0),
|
|
"Expected both workspaces to remain after cancelling Cmd+Shift+W multi-close. list=\(socketCommand("list_workspaces") ?? "<nil>")"
|
|
)
|
|
}
|
|
|
|
private func ensureForegroundAfterLaunch(_ app: XCUIApplication, timeout: TimeInterval) -> Bool {
|
|
if app.wait(for: .runningForeground, timeout: timeout) {
|
|
return true
|
|
}
|
|
if app.state == .runningBackground {
|
|
app.activate()
|
|
return app.wait(for: .runningForeground, timeout: 6.0)
|
|
}
|
|
return false
|
|
}
|
|
|
|
private func waitForSocketPong(timeout: TimeInterval) -> Bool {
|
|
let expectation = XCTNSPredicateExpectation(
|
|
predicate: NSPredicate { _, _ in
|
|
self.socketCommand("ping") == "PONG"
|
|
},
|
|
object: NSObject()
|
|
)
|
|
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
|
|
}
|
|
|
|
private func waitForWorkspaceCount(_ expectedCount: Int, timeout: TimeInterval) -> Bool {
|
|
let expectation = XCTNSPredicateExpectation(
|
|
predicate: NSPredicate { _, _ in
|
|
self.workspaceCount() == expectedCount
|
|
},
|
|
object: NSObject()
|
|
)
|
|
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
|
|
}
|
|
|
|
private func workspaceCount() -> Int {
|
|
guard let response = socketCommand("list_workspaces") else { return -1 }
|
|
if response == "No workspaces" {
|
|
return 0
|
|
}
|
|
return response
|
|
.split(separator: "\n")
|
|
.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
|
|
.count
|
|
}
|
|
|
|
private func socketCommand(_ cmd: String) -> String? {
|
|
let nc = "/usr/bin/nc"
|
|
guard FileManager.default.isExecutableFile(atPath: nc) else { return nil }
|
|
|
|
let proc = Process()
|
|
proc.executableURL = URL(fileURLWithPath: nc)
|
|
proc.arguments = ["-U", socketPath, "-w", "2"]
|
|
|
|
let inPipe = Pipe()
|
|
let outPipe = Pipe()
|
|
let errPipe = Pipe()
|
|
proc.standardInput = inPipe
|
|
proc.standardOutput = outPipe
|
|
proc.standardError = errPipe
|
|
|
|
do {
|
|
try proc.run()
|
|
} catch {
|
|
return nil
|
|
}
|
|
|
|
if let data = (cmd + "\n").data(using: .utf8) {
|
|
inPipe.fileHandleForWriting.write(data)
|
|
}
|
|
inPipe.fileHandleForWriting.closeFile()
|
|
|
|
proc.waitUntilExit()
|
|
|
|
let outData = outPipe.fileHandleForReading.readDataToEndOfFile()
|
|
guard let outStr = String(data: outData, encoding: .utf8) else { return nil }
|
|
return outStr.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
|
|
private func isCloseWorkspacesAlertPresent(app: XCUIApplication) -> Bool {
|
|
if closeWorkspacesDialog(app: app).exists { return true }
|
|
if closeWorkspacesAlert(app: app).exists { return true }
|
|
return app.staticTexts["Close workspaces?"].exists
|
|
}
|
|
|
|
private func waitForCloseWorkspacesAlert(app: XCUIApplication, timeout: TimeInterval) -> Bool {
|
|
let expectation = XCTNSPredicateExpectation(
|
|
predicate: NSPredicate { _, _ in
|
|
self.isCloseWorkspacesAlertPresent(app: app)
|
|
},
|
|
object: NSObject()
|
|
)
|
|
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
|
|
}
|
|
|
|
private func clickCancelOnCloseWorkspacesAlert(app: XCUIApplication) {
|
|
let dialog = closeWorkspacesDialog(app: app)
|
|
if dialog.exists {
|
|
dialog.buttons["Cancel"].firstMatch.click()
|
|
return
|
|
}
|
|
let alert = closeWorkspacesAlert(app: app)
|
|
if alert.exists {
|
|
alert.buttons["Cancel"].firstMatch.click()
|
|
return
|
|
}
|
|
let anyDialog = app.dialogs.firstMatch
|
|
if anyDialog.exists, anyDialog.buttons["Cancel"].exists {
|
|
anyDialog.buttons["Cancel"].firstMatch.click()
|
|
}
|
|
}
|
|
|
|
private func closeWorkspacesDialog(app: XCUIApplication) -> XCUIElement {
|
|
app.dialogs.containing(.staticText, identifier: "Close workspaces?").firstMatch
|
|
}
|
|
|
|
private func closeWorkspacesAlert(app: XCUIApplication) -> XCUIElement {
|
|
app.alerts.containing(.staticText, identifier: "Close workspaces?").firstMatch
|
|
}
|
|
}
|