Files
lanework/KanbanTests/WindowPlacementTests.swift
rzen fccdf56cf4 Stand up the window architecture — welcome, board, card
Four scenes (welcome, restore bootstrap, board group, card group) with
system restoration disabled in favor of the registry's open-now flags:
set when a window actually opens, cleared only on user close, so quit —
and crash — leave exactly the restoration set behind. AppModel joins
windows to sessions (shared store, registry record, card refs, held
security scope); CloseFlushCoordinator pins 02's strict close order as
a seam-injected machine (card sessions end, windows drain, store
flushes, record stamps, teardown) with named slots where m6/m7 flushes
land. HostedWindowController proxies — never replaces — SwiftUI's
window delegate to intercept windowShouldClose for the flush, report
frames, and place saved frames onto live screens. Card windows are
(board path, case-folded card id) values: reopen focuses, and a
snapshot-pure fate function dismisses on delete, tombstone, tombstoned
lane, or cross-board move.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
2026-07-27 07:47:11 -04:00

57 lines
2.8 KiB
Swift

import AppKit
import Testing
@testable import Kanban
/// Per-board frame memory has one rule that is not "put it back where it was": a frame saved on a
/// display that is no longer attached must land somewhere visible (02-architecture.md § Windows).
/// That rule is untestable against real hardware — the interesting case *is* the monitor that is not
/// plugged in — which is why the decision takes its screens as an argument.
@MainActor
@Suite("Window placement")
struct WindowPlacementTests {
private let laptop = NSRect(x: 0, y: 0, width: 1512, height: 916)
private let external = NSRect(x: 1512, y: 0, width: 2560, height: 1415)
@Test("A frame on an attached screen is restored exactly")
func aVisibleFrameIsUntouched() {
let saved = WindowFrame(x: 120, y: 80, width: 1200, height: 700)
let placed = HostedWindowController.placement(for: saved, onScreens: [laptop, external], fallback: laptop)
#expect(placed == NSRect(x: 120, y: 80, width: 1200, height: 700))
}
@Test("A frame straddling two screens is still where the user left it")
func aStraddlingFrameIsUntouched() {
// AppKit's own `constrainFrameRect(_:to:)` nudges the remainder into view when the frame is
// set, so intersection — not containment — is the right test: a window hanging slightly off
// an edge is a place, not a problem.
let saved = WindowFrame(x: 1400, y: 100, width: 900, height: 600)
let placed = HostedWindowController.placement(for: saved, onScreens: [laptop, external], fallback: laptop)
#expect(placed.origin.x == 1400)
}
@Test("A frame on a screen that is gone keeps its size and centers on the fallback")
func aVanishedScreenRelocates() {
let saved = WindowFrame(x: 3000, y: 200, width: 1000, height: 600)
let placed = HostedWindowController.placement(for: saved, onScreens: [laptop], fallback: laptop)
// Size is a preference and survives; position is a place, and the place stopped existing.
#expect(placed.size == NSSize(width: 1000, height: 600))
#expect(placed.midX == laptop.midX)
#expect(placed.midY == laptop.midY)
#expect(laptop.intersects(placed))
}
@Test("With no screens at all the fallback still produces a frame")
func noScreensStillPlaces() {
// Not a real state, but the accessor's `NSScreen.screens` can be empty during a display
// reconfiguration, and returning something sane beats a window at the saved coordinates of a
// display nobody has.
let saved = WindowFrame(x: 4000, y: 4000, width: 800, height: 500)
let placed = HostedWindowController.placement(for: saved, onScreens: [], fallback: laptop)
#expect(placed.size == NSSize(width: 800, height: 500))
#expect(laptop.contains(placed.origin))
}
}