diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 4d748c9529a4..5c9cb90adc18 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -10929,6 +10929,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent guard tabManager != nil else { return } let startWithHiddenSidebar = env["CMUX_UI_TEST_BONSPLIT_START_WITH_HIDDEN_SIDEBAR"] == "1" let showRightSidebar = env["CMUX_UI_TEST_BONSPLIT_SHOW_RIGHT_SIDEBAR"] == "1" + let fourTabSetup = env["CMUX_UI_TEST_BONSPLIT_FOUR_TAB_SETUP"] == "1" let deadline = Date().addingTimeInterval(20.0) func mainWindowContextForUITest() -> (window: NSWindow, context: MainWindowContext)? { @@ -10993,6 +10994,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent let workspaceTitle = "UITest Workspace" let alphaTitle = "UITest Alpha" let betaTitle = "UITest Beta" + let gammaTitle = "UITest Gamma" + let deltaTitle = "UITest Delta" tabManager.setCustomTitle(tabId: workspace.id, title: workspaceTitle) workspace.setPanelCustomTitle(panelId: alphaPanelId, title: alphaTitle) tabManager.newSurface() @@ -11003,6 +11006,30 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent } workspace.setPanelCustomTitle(panelId: betaPanelId, title: betaTitle) + var gammaPanelId: UUID? + var deltaPanelId: UUID? + if fourTabSetup { + tabManager.newSurface() + guard let createdGammaPanelId = workspace.focusedPanelId, + createdGammaPanelId != alphaPanelId, + createdGammaPanelId != betaPanelId else { + self.writeBonsplitTabDragUITestData(["setupError": "Failed to create third surface"]) + return + } + gammaPanelId = createdGammaPanelId + workspace.setPanelCustomTitle(panelId: createdGammaPanelId, title: gammaTitle) + + tabManager.newSurface() + guard let createdDeltaPanelId = workspace.focusedPanelId, + createdDeltaPanelId != alphaPanelId, + createdDeltaPanelId != betaPanelId, + createdDeltaPanelId != createdGammaPanelId else { + self.writeBonsplitTabDragUITestData(["setupError": "Failed to create fourth surface"]) + return + } + deltaPanelId = createdDeltaPanelId + workspace.setPanelCustomTitle(panelId: createdDeltaPanelId, title: deltaTitle) + } if let rawActionButtonCount = env["CMUX_UI_TEST_BONSPLIT_ACTION_BUTTON_COUNT"], let requestedActionButtonCount = Int(rawActionButtonCount), requestedActionButtonCount > 0 { @@ -11053,8 +11080,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent "workspaceTitle": workspaceTitle, "alphaTitle": alphaTitle, "betaTitle": betaTitle, + "gammaTitle": fourTabSetup ? gammaTitle : "", + "deltaTitle": fourTabSetup ? deltaTitle : "", "alphaPanelId": alphaPanelId.uuidString, "betaPanelId": betaPanelId.uuidString, + "gammaPanelId": gammaPanelId?.uuidString ?? "", + "deltaPanelId": deltaPanelId?.uuidString ?? "", ]) self.startBonsplitTabDragUITestRecorder( workspaceId: workspace.id, diff --git a/cmuxUITests/BonsplitTabDragUITests.swift b/cmuxUITests/BonsplitTabDragUITests.swift index ed81359d53b7..ca1f6cb6da0b 100644 --- a/cmuxUITests/BonsplitTabDragUITests.swift +++ b/cmuxUITests/BonsplitTabDragUITests.swift @@ -65,6 +65,102 @@ final class BonsplitTabDragUITests: XCTestCase { XCTAssertEqual(window.frame.origin.y, windowFrameBeforeDrag.origin.y, accuracy: 2.0, "Expected tab drag not to move the window vertically") } + func testMinimalModeReordersLaterTabToMiddleIndex() { + let (app, dataPath) = launchConfiguredApp(fourTabSetup: true) + + XCTAssertTrue( + ensureAppRunningAfterLaunch(app, timeout: launchTimeout), + "Expected app to launch for later-to-middle Bonsplit tab drag UI test. state=\(app.state.rawValue)" + ) + XCTAssertTrue(waitForAnyJSON(atPath: dataPath, timeout: setupTimeout), "Expected tab-drag setup data at \(dataPath)") + guard let ready = waitForJSONKey("ready", equals: "1", atPath: dataPath, timeout: setupTimeout) else { + XCTFail("Timed out waiting for ready=1. data=\(loadJSON(atPath: dataPath) ?? [:])") + return + } + + if let setupError = ready["setupError"], !setupError.isEmpty { + XCTFail("Setup failed: \(setupError)") + return + } + + let alphaTitle = ready["alphaTitle"] ?? "UITest Alpha" + let betaTitle = ready["betaTitle"] ?? "UITest Beta" + let gammaTitle = ready["gammaTitle"] ?? "UITest Gamma" + let deltaTitle = ready["deltaTitle"] ?? "UITest Delta" + let initialOrder = "\(alphaTitle)|\(betaTitle)|\(gammaTitle)|\(deltaTitle)" + let expectedOrder = "\(alphaTitle)|\(deltaTitle)|\(betaTitle)|\(gammaTitle)" + let window = app.windows.element(boundBy: 0) + let betaTab = app.buttons[betaTitle] + let deltaTab = app.buttons[deltaTitle] + + XCTAssertTrue(window.waitForExistence(timeout: 5.0), "Expected main window to exist") + XCTAssertTrue(betaTab.waitForExistence(timeout: 5.0), "Expected beta tab to exist") + XCTAssertTrue(deltaTab.waitForExistence(timeout: 5.0), "Expected delta tab to exist") + XCTAssertTrue( + waitForJSONKey("trackedPaneTabTitles", equals: initialOrder, atPath: dataPath, timeout: 5.0) != nil, + "Expected initial tracked tab order to be \(initialOrder). data=\(loadJSON(atPath: dataPath) ?? [:])" + ) + + dragTab(deltaTab, before: betaTab) + + XCTAssertTrue( + waitForJSONKey("trackedPaneTabTitles", equals: expectedOrder, atPath: dataPath, timeout: 5.0) != nil, + "Expected dragging the later tab into the middle to produce \(expectedOrder). data=\(loadJSON(atPath: dataPath) ?? [:])" + ) + XCTAssertTrue( + waitForCondition(timeout: 5.0) { deltaTab.frame.minX < betaTab.frame.minX }, + "Expected delta tab to settle before beta without appending to the end. beta=\(betaTab.frame) delta=\(deltaTab.frame)" + ) + } + + func testMinimalModeReordersEarlierTabToMiddleIndex() { + let (app, dataPath) = launchConfiguredApp(fourTabSetup: true) + + XCTAssertTrue( + ensureAppRunningAfterLaunch(app, timeout: launchTimeout), + "Expected app to launch for earlier-to-middle Bonsplit tab drag UI test. state=\(app.state.rawValue)" + ) + XCTAssertTrue(waitForAnyJSON(atPath: dataPath, timeout: setupTimeout), "Expected tab-drag setup data at \(dataPath)") + guard let ready = waitForJSONKey("ready", equals: "1", atPath: dataPath, timeout: setupTimeout) else { + XCTFail("Timed out waiting for ready=1. data=\(loadJSON(atPath: dataPath) ?? [:])") + return + } + + if let setupError = ready["setupError"], !setupError.isEmpty { + XCTFail("Setup failed: \(setupError)") + return + } + + let alphaTitle = ready["alphaTitle"] ?? "UITest Alpha" + let betaTitle = ready["betaTitle"] ?? "UITest Beta" + let gammaTitle = ready["gammaTitle"] ?? "UITest Gamma" + let deltaTitle = ready["deltaTitle"] ?? "UITest Delta" + let initialOrder = "\(alphaTitle)|\(betaTitle)|\(gammaTitle)|\(deltaTitle)" + let expectedOrder = "\(betaTitle)|\(gammaTitle)|\(alphaTitle)|\(deltaTitle)" + let window = app.windows.element(boundBy: 0) + let alphaTab = app.buttons[alphaTitle] + let deltaTab = app.buttons[deltaTitle] + + XCTAssertTrue(window.waitForExistence(timeout: 5.0), "Expected main window to exist") + XCTAssertTrue(alphaTab.waitForExistence(timeout: 5.0), "Expected alpha tab to exist") + XCTAssertTrue(deltaTab.waitForExistence(timeout: 5.0), "Expected delta tab to exist") + XCTAssertTrue( + waitForJSONKey("trackedPaneTabTitles", equals: initialOrder, atPath: dataPath, timeout: 5.0) != nil, + "Expected initial tracked tab order to be \(initialOrder). data=\(loadJSON(atPath: dataPath) ?? [:])" + ) + + dragTab(alphaTab, before: deltaTab) + + XCTAssertTrue( + waitForJSONKey("trackedPaneTabTitles", equals: expectedOrder, atPath: dataPath, timeout: 5.0) != nil, + "Expected dragging the earlier tab into the middle to produce \(expectedOrder). data=\(loadJSON(atPath: dataPath) ?? [:])" + ) + XCTAssertTrue( + waitForCondition(timeout: 5.0) { alphaTab.frame.minX < deltaTab.frame.minX }, + "Expected alpha tab to settle before delta without appending to the end. alpha=\(alphaTab.frame) delta=\(deltaTab.frame)" + ) + } + func testMinimalModePlacesPaneTabBarAtTopEdge() { let (app, dataPath) = launchConfiguredApp() @@ -876,7 +972,8 @@ final class BonsplitTabDragUITests: XCTestCase { showRightSidebar: Bool = false, alwaysShowShortcutHints: Bool = false, windowSize: String? = nil, - actionButtonCount: Int? = nil + actionButtonCount: Int? = nil, + fourTabSetup: Bool = false ) -> (XCUIApplication, String) { let app = XCUIApplication.cmuxTestApplication() let dataPath = "/tmp/cmux-ui-test-bonsplit-tab-drag-\(UUID().uuidString).json" @@ -894,6 +991,9 @@ final class BonsplitTabDragUITests: XCTestCase { if let actionButtonCount { app.launchEnvironment["CMUX_UI_TEST_BONSPLIT_ACTION_BUTTON_COUNT"] = String(actionButtonCount) } + if fourTabSetup { + app.launchEnvironment["CMUX_UI_TEST_BONSPLIT_FOUR_TAB_SETUP"] = "1" + } if showRightSidebar { app.launchEnvironment["CMUX_UI_TEST_BONSPLIT_SHOW_RIGHT_SIDEBAR"] = "1" } diff --git a/vendor/bonsplit b/vendor/bonsplit index e3837a787968..529913b7a49c 160000 --- a/vendor/bonsplit +++ b/vendor/bonsplit @@ -1 +1 @@ -Subproject commit e3837a787968d8235106d12418b653e8d1e2619c +Subproject commit 529913b7a49c39669414578f1f32ccf2df47f0f8