fix: hide all window in TFM default tests
This commit is contained in:
1 parent
14ec76db99
commit
d9968bcc3d
9 files changed
+82
-25
No files matched your search
@@ -13,6 +13,25 @@ import TermindRemoteInfrastructure
|
||||
|
||||
@main
|
||||
struct TermindApp: App {
|
||||
static let isRunningTests: Bool = {
|
||||
#if DEBUG
|
||||
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
}()
|
||||
|
||||
static var suppressesAutomaticTestWindows: Bool {
|
||||
#if DEBUG
|
||||
guard isRunningTests, ProcessInfo.processInfo.environment["TERMIND_WINDOW_TESTS"] != "1" else {
|
||||
return false
|
||||
}
|
||||
return ScreenshotDemoConfiguration.active == nil
|
||||
#else
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Hosts metadata is stored in SwiftData; secrets stay behind Keychain
|
||||
/// references held by the value-type projections used throughout the UI.
|
||||
private let modelContainer: ModelContainer
|
||||
@@ -29,8 +48,7 @@ struct TermindApp: App {
|
||||
launchLanguage = AppLanguage.stored()
|
||||
#endif
|
||||
do {
|
||||
let isRunningTests =
|
||||
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
|
||||
let isRunningTests = Self.isRunningTests
|
||||
#if DEBUG
|
||||
let usesInMemoryStore = isRunningTests || screenshotDemo != nil
|
||||
#else
|
||||
@@ -188,7 +206,11 @@ struct TermindApp: App {
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
.restorationBehavior(ScreenshotDemoConfiguration.active == nil ? .automatic : .disabled)
|
||||
// XCTest launches the real app. Gating individual tests does not stop
|
||||
// SwiftUI from opening or restoring the host's own main window.
|
||||
.defaultLaunchBehavior(Self.suppressesAutomaticTestWindows ? .suppressed : .automatic)
|
||||
.restorationBehavior(
|
||||
Self.isRunningTests || ScreenshotDemoConfiguration.active != nil ? .disabled : .automatic)
|
||||
#endif
|
||||
.defaultSize(app.windowSizeStore.defaultSize)
|
||||
// All main pages share one content floor. Opening the sidebar lets
|
||||
@@ -202,6 +224,10 @@ struct TermindApp: App {
|
||||
.environment(\.locale, launchLanguage.locale)
|
||||
.modelContainer(modelContainer)
|
||||
}
|
||||
#if DEBUG
|
||||
.defaultLaunchBehavior(Self.suppressesAutomaticTestWindows ? .suppressed : .automatic)
|
||||
.restorationBehavior(Self.isRunningTests ? .disabled : .automatic)
|
||||
#endif
|
||||
.defaultSize(width: 560, height: 240)
|
||||
.windowResizability(.contentSize)
|
||||
// SwiftUI lists every `Window` scene in the Window menu so people can
|
||||
@@ -247,7 +273,9 @@ struct TermindApp: App {
|
||||
weak var app: AppModel? {
|
||||
didSet {
|
||||
guard let app else { return }
|
||||
guard ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] == nil else { return }
|
||||
#if DEBUG
|
||||
guard !TermindApp.isRunningTests else { return }
|
||||
#endif
|
||||
app.serverMetrics.start()
|
||||
metricsActivity.start(app.serverMetrics)
|
||||
metricsMenu.openMainWindow = { [weak app] in app?.mainWindows.openMetrics() }
|
||||
@@ -321,12 +349,19 @@ struct TermindApp: App {
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
#if DEBUG
|
||||
// Closing an offscreen fixture must not terminate the test runner.
|
||||
guard !TermindApp.isRunningTests else { return false }
|
||||
#endif
|
||||
let menuEnabled =
|
||||
UserDefaults.standard.object(forKey: DesktopSettingsKey.showMetricsInMenuBar) as? Bool ?? true
|
||||
return !menuEnabled && app?.serverMetrics.keepsRunning != true
|
||||
}
|
||||
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
#if DEBUG
|
||||
guard !TermindApp.suppressesAutomaticTestWindows else { return false }
|
||||
#endif
|
||||
guard !flag else { return true }
|
||||
return app?.mainWindows.handleReopen() ?? true
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ struct AgentHostAuthenticationTests {
|
||||
#expect(store.key(key.id)?.requiresPassphraseEntry == true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func savedIdentityWithProtectedKeyRequestsOnlyPassphraseExternally() async throws {
|
||||
let keychain = MemoryHostSecretStore()
|
||||
let reference = "termind.key.\(UUID().uuidString)"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import AppKit
|
||||
import Testing
|
||||
|
||||
@testable import Termind
|
||||
|
||||
@MainActor
|
||||
struct AppTestHostTests {
|
||||
@Test
|
||||
func defaultTestHostDoesNotPresentWindowsOrReopenTheMainScene() {
|
||||
guard TermindApp.suppressesAutomaticTestWindows else { return }
|
||||
#expect(NSApp.windows.allSatisfy { !$0.isVisible })
|
||||
let delegate = AppDelegate()
|
||||
#expect(!delegate.applicationShouldHandleReopen(NSApp, hasVisibleWindows: false))
|
||||
}
|
||||
|
||||
@Test
|
||||
func closingTheLastFixtureWindowDoesNotTerminateTheTestHost() {
|
||||
#expect(!AppDelegate().applicationShouldTerminateAfterLastWindowClosed(NSApp))
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ struct DockMenuTests {
|
||||
#expect(try #require(delegate.applicationDockMenu(NSApp)).items[0].isEnabled == false)
|
||||
}
|
||||
|
||||
@Test(arguments: [false, true])
|
||||
@Test(.requiresVisibleWindow, arguments: [false, true])
|
||||
func repeatedDockActionsCreateDistinctWelcomeWindowsWithoutAnExistingWindow(closePreviousWindow: Bool) throws {
|
||||
let app = AppModel(hosts: HostLibraryModel())
|
||||
let delegate = AppDelegate()
|
||||
@@ -79,7 +79,7 @@ struct DockMenuTests {
|
||||
#expect(secondModel.assistantQuery.isEmpty)
|
||||
}
|
||||
|
||||
@Test(arguments: [false, true])
|
||||
@Test(.requiresVisibleWindow, arguments: [false, true])
|
||||
func dockNewWindowPreservesTheExistingPageAndWorkspaceEvenInFullScreen(fullScreen: Bool) throws {
|
||||
let host = Host(name: "Server", host: "server.example")
|
||||
let app = AppModel(hosts: HostLibraryModel(hosts: [host]))
|
||||
|
||||
@@ -17,7 +17,7 @@ import struct TermindDomain.Host
|
||||
@Suite(.serialized)
|
||||
@MainActor
|
||||
struct ExternalAuthCoordinatorTests {
|
||||
@Test func interactiveResponsesAreScopedAndInformationalRoundsComplete() async throws {
|
||||
@Test(.requiresVisibleWindow) func interactiveResponsesAreScopedAndInformationalRoundsComplete() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let challenge = SSHKeyboardInteractiveChallenge(
|
||||
connectionID: UUID(), hostID: UUID(), hostname: "server.example", port: 22,
|
||||
@@ -38,7 +38,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.current == nil)
|
||||
}
|
||||
|
||||
@Test func stoppingMCPPreservesAppAuthentication() async throws {
|
||||
@Test(.requiresVisibleWindow) func stoppingMCPPreservesAppAuthentication() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let challenge = SSHKeyboardInteractiveChallenge(
|
||||
connectionID: UUID(), hostID: UUID(), hostname: "server.example", port: 22,
|
||||
@@ -89,7 +89,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(window.standardWindowButton(.zoomButton)?.isHidden == false)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func popupQueueResumesInFIFOOrder() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let runID = UUID()
|
||||
@@ -117,7 +117,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func protectedKeyCanRaisePassphraseImmediatelyAfterIdentity() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let handler = coordinator.handler(clientName: "Codex", runID: UUID())
|
||||
@@ -170,7 +170,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func closingThePopupCancelsEveryWaiter() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let task = Task {
|
||||
@@ -186,7 +186,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func staleWindowDisappearanceCannotCancelANewerPassphraseRequest() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let firstRequest = authenticationRequest(name: "First")
|
||||
@@ -226,7 +226,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func secretRequestsUseTheSamePopupQueueAndRejectOrdinaryQuestions() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let runID = UUID()
|
||||
@@ -257,7 +257,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func cancellingAQueuedSessionDoesNotRepresentTheCurrentPopup() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let currentRunID = UUID()
|
||||
@@ -286,7 +286,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(await current.value == .cancelled)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func visibleSecretTimesOutAndLeavesNoPendingPopup() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let request = secretRequest(timeout: .milliseconds(40))
|
||||
@@ -299,7 +299,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func queuedSecretDoesNotSpendItsTimeoutBehindSSHPrompt() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let sshRequest = authenticationRequest(name: "Current")
|
||||
@@ -324,7 +324,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func answeredSecretCannotLaterRetireANewerPopup() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let request = secretRequest(timeout: .milliseconds(40))
|
||||
@@ -355,7 +355,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(await ssh.value == .cancelled)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func hostKeyUsesPopupQueueAndReturnsSelectedTrustScope() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let request = hostKeyRequest()
|
||||
@@ -376,7 +376,7 @@ struct ExternalAuthCoordinatorTests {
|
||||
#expect(coordinator.pending.isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func closingHostKeyPopupDeniesTheWaitingCall() async throws {
|
||||
let coordinator = ExternalAuthCoordinator()
|
||||
let task = Task {
|
||||
|
||||
@@ -208,7 +208,7 @@ struct MCPServerSessionTests {
|
||||
#expect(message.contains("\"id\":1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func deletingASessionCancelsItsSecretPopupWithoutResurrectingTheSession() async throws {
|
||||
let authentication = ExternalAuthCoordinator()
|
||||
let protocolServer = MCPServerProtocol(
|
||||
|
||||
@@ -190,7 +190,7 @@ struct MCPServerToolsTests {
|
||||
#expect(!result.isError)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func secretPromptReturnsOnlyASessionBoundOneShotHandle() async throws {
|
||||
let authentication = ExternalAuthCoordinator()
|
||||
let server = makeMCPServerTools(externalAuthentication: authentication)
|
||||
@@ -251,7 +251,7 @@ struct MCPServerToolsTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func timedOutSecretPromptReturnsAnErrorAndIssuesNoHandle() async throws {
|
||||
let authentication = ExternalAuthCoordinator()
|
||||
let server = makeMCPServerTools(externalAuthentication: authentication)
|
||||
@@ -286,7 +286,7 @@ struct MCPServerToolsTests {
|
||||
#expect(await broker.count == 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(.requiresVisibleWindow)
|
||||
func endingASessionCancelsItsPromptAndDiscardsSecrets() async throws {
|
||||
let authentication = ExternalAuthCoordinator()
|
||||
let server = makeMCPServerTools(externalAuthentication: authentication)
|
||||
|
||||
@@ -41,7 +41,8 @@ struct SFTPApplicationOpenerTests {
|
||||
#expect(SFTPApplicationOpener.appStoreSearchURL(for: "README") == nil)
|
||||
}
|
||||
|
||||
@Test func missingApplicationPropagatesLaunchFailure() async throws {
|
||||
// Launch Services may show a system alert even though this open request is expected to fail.
|
||||
@Test(.requiresVisibleWindow) func missingApplicationPropagatesLaunchFailure() async throws {
|
||||
let directory = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
||||
defer { try? FileManager.default.removeItem(at: directory) }
|
||||
|
||||
@@ -3,6 +3,7 @@ import Testing
|
||||
|
||||
extension Trait where Self == ConditionTrait {
|
||||
/// Window presentation and focus changes require an explicit opt-in, even on a machine with a display.
|
||||
/// This includes Dock actions, external authentication requests, and Launch Services alerts.
|
||||
static var requiresVisibleWindow: Self {
|
||||
.enabled(
|
||||
if: ProcessInfo.processInfo.environment["TERMIND_WINDOW_TESTS"] == "1",
|
||||
|
||||
Reference in new issue
Block a user