@@ -15,6 +15,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
1515 @MainActor func testWindowCascading( ) async throws {
1616 try updateConfig (
1717 """
18+ window-width = 30
19+ window-height = 10
1820 title = " GhosttyWindowPositionUITests "
1921 """
2022 )
@@ -46,6 +48,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
4648
4749 XCTAssertEqual ( windowFrame2. minX, windowFrame. minX + 30 , accuracy: 5 , " New window should be on the right " )
4850
51+ XCTAssertEqual ( windowFrame2. minY, windowFrame. minY + 30 , accuracy: 5 , " New window should be on the bottom right " )
52+
4953 app. typeKey ( " n " , modifierFlags: [ . command] )
5054
5155 let window3 = app. windows. firstMatch
@@ -55,6 +59,8 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
5559
5660 XCTAssertEqual ( windowFrame3. minX, windowFrame2. minX + 30 , accuracy: 5 , " New window should be on the right " )
5761
62+ XCTAssertEqual ( windowFrame3. minY, windowFrame2. minY + 30 , accuracy: 5 , " New window should be on the bottom right " )
63+
5864 app. typeKey ( " n " , modifierFlags: [ . command] )
5965
6066 let window4 = app. windows. firstMatch
@@ -63,6 +69,111 @@ final class GhosttyWindowPositionUITests: GhosttyCustomConfigCase {
6369 XCTAssertNotEqual ( windowFrame3, windowFrame4, " New window should have moved " )
6470
6571 XCTAssertEqual ( windowFrame4. minX, windowFrame3. minX + 30 , accuracy: 5 , " New window should be on the right " )
72+
73+ XCTAssertEqual ( windowFrame4. minY, windowFrame3. minY + 30 , accuracy: 5 , " New window should be on the bottom right " )
74+ }
75+
76+ @MainActor func testDragSplitWindowPosition( ) async throws {
77+ try updateConfig (
78+ """
79+ window-width = 40
80+ window-height = 20
81+ title = " GhosttyWindowPositionUITests "
82+ macos-titlebar-style = hidden
83+ """
84+ )
85+
86+ let app = try ghosttyApplication ( )
87+ // Suppress Restoration
88+ app. launchArguments += [ " -NSQuitAlwaysKeepsWindows " , " NO " ]
89+ // Clean run
90+ app. launchEnvironment [ " GHOSTTY_CLEAR_USER_DEFAULTS " ] = " YES "
91+
92+ app. launch ( ) // window in the center
93+
94+ let window = app. windows. firstMatch
95+ XCTAssertTrue ( window. waitForExistence ( timeout: 5 ) , " New window should appear " )
96+
97+ // remove fixed size
98+ try updateConfig (
99+ """
100+ title = " GhosttyWindowPositionUITests "
101+ macos-titlebar-style = hidden
102+ """
103+ )
104+ app. typeKey ( " , " , modifierFlags: [ . command, . shift] )
105+
106+ app. typeKey ( " d " , modifierFlags: [ . command] )
107+
108+ let rightSplit = app. groups [ " Right pane " ]
109+ let rightFrame = rightSplit. frame
110+
111+ let sourcePos = rightSplit. coordinate ( withNormalizedOffset: . zero)
112+ . withOffset ( . init( dx: rightFrame. size. width / 2 , dy: 3 ) )
113+
114+ let targetPos = rightSplit. coordinate ( withNormalizedOffset: . zero)
115+ . withOffset ( . init( dx: rightFrame. size. width + 100 , dy: 0 ) )
116+
117+ sourcePos. click ( forDuration: 0.2 , thenDragTo: targetPos)
118+
119+ let window2 = app. windows. firstMatch
120+ XCTAssertTrue ( window2. waitForExistence ( timeout: 5 ) , " New window should appear " )
121+ let windowFrame2 = window2. frame
122+
123+ try await Task . sleep ( for: . seconds( 0.5 ) )
124+
125+ XCTAssertEqual ( windowFrame2. minX, rightFrame. maxX + 100 , accuracy: 5 , " New window should be target position " )
126+ XCTAssertEqual ( windowFrame2. minY, rightFrame. minY, accuracy: 5 , " New window should be target position " )
127+ XCTAssertEqual ( windowFrame2. width, rightFrame. width, accuracy: 5 , " New window should use size from config " )
128+ XCTAssertEqual ( windowFrame2. height, rightFrame. height, accuracy: 5 , " New window should use size from config " )
129+ }
130+
131+ @MainActor func testDragSplitWindowPositionWithFixedSize( ) async throws {
132+ try updateConfig (
133+ """
134+ window-width = 40
135+ window-height = 20
136+ title = " GhosttyWindowPositionUITests "
137+ macos-titlebar-style = hidden
138+ """
139+ )
140+
141+ let app = try ghosttyApplication ( )
142+ // Suppress Restoration
143+ app. launchArguments += [ " -NSQuitAlwaysKeepsWindows " , " NO " ]
144+ // Clean run
145+ app. launchEnvironment [ " GHOSTTY_CLEAR_USER_DEFAULTS " ] = " YES "
146+
147+ app. launch ( ) // window in the center
148+
149+ let window = app. windows. firstMatch
150+ XCTAssertTrue ( window. waitForExistence ( timeout: 5 ) , " New window should appear " )
151+ let windowFrame = window. frame
152+
153+ app. typeKey ( " d " , modifierFlags: [ . command] )
154+
155+ let rightSplit = app. groups [ " Right pane " ]
156+ let rightFrame = rightSplit. frame
157+
158+ let sourcePos = rightSplit. coordinate ( withNormalizedOffset: . zero)
159+ . withOffset ( . init( dx: rightFrame. size. width / 2 , dy: 3 ) )
160+
161+ let targetPos = rightSplit. coordinate ( withNormalizedOffset: . zero)
162+ . withOffset ( . init( dx: rightFrame. size. width + 100 , dy: 0 ) )
163+
164+ sourcePos. click ( forDuration: 0.2 , thenDragTo: targetPos)
165+
166+ let window2 = app. windows. firstMatch
167+ XCTAssertTrue ( window2. waitForExistence ( timeout: 5 ) , " New window should appear " )
168+ let windowFrame2 = window2. frame
169+
170+ try await Task . sleep ( for: . seconds( 0.5 ) )
171+
172+ XCTAssertEqual ( windowFrame2. minX, rightFrame. maxX + 100 , accuracy: 5 , " New window should be target position " )
173+ XCTAssertEqual ( windowFrame2. minY, rightFrame. minY, accuracy: 5 , " New window should be target position " )
174+ XCTAssertEqual ( windowFrame2. width, windowFrame. width, accuracy: 5 , " New window should use size from config " )
175+ // We're still using right frame, because of the debug banner
176+ XCTAssertEqual ( windowFrame2. height, rightFrame. height, accuracy: 5 , " New window should use size from config " )
66177 }
67178
68179 // MARK: - Restore round-trip per titlebar style
0 commit comments