diff --git a/peekaboo-cli/Tests/peekabooTests/LocalOnlyTests.swift b/peekaboo-cli/Tests/peekabooTests/LocalOnlyTests.swift index a9adc0f..3dc2b12 100644 --- a/peekaboo-cli/Tests/peekabooTests/LocalOnlyTests.swift +++ b/peekaboo-cli/Tests/peekabooTests/LocalOnlyTests.swift @@ -24,7 +24,7 @@ struct LocalIntegrationTests { // Check if test host is already running let runningApps = NSWorkspace.shared.runningApplications if let existingApp = runningApps.first(where: { $0.bundleIdentifier == Self.testHostBundleId }) { - existingApp.activate(options: .activateIgnoringOtherApps) + existingApp.activate() try await Task.sleep(nanoseconds: 500_000_000) // 0.5s return existingApp } @@ -36,11 +36,9 @@ struct LocalIntegrationTests { throw TestError.invalidPath(testHostPath) } - let app = try NSWorkspace.shared.launchApplication( - at: url, - options: .default, - configuration: [:] - ) + // Use modern NSWorkspace API + let configuration = NSWorkspace.OpenConfiguration() + let app = try await NSWorkspace.shared.openApplication(at: url, configuration: configuration) // Wait for app to be ready try await Task.sleep(nanoseconds: 1_000_000_000) // 1s @@ -80,7 +78,7 @@ struct LocalIntegrationTests { @Test("Capture test host window screenshot", .tags(.screenshot)) func captureTestHostWindow() async throws { - let app = try await launchTestHost() + _ = try await launchTestHost() defer { terminateTestHost() } // Wait for window to be visible @@ -138,7 +136,7 @@ struct LocalIntegrationTests { defer { terminateTestHost() } // Ensure test host is in foreground - app.activate(options: .activateIgnoringOtherApps) + app.activate() try await Task.sleep(nanoseconds: 500_000_000) // 0.5s // Capture the main screen @@ -172,7 +170,7 @@ struct LocalIntegrationTests { @Test("Test permission dialogs", .tags(.permissions)) func permissionDialogs() async throws { - let app = try await launchTestHost() + _ = try await launchTestHost() defer { terminateTestHost() } // Check current permissions @@ -242,14 +240,13 @@ struct LocalIntegrationTests { defer { terminateTestHost() } // Make sure test host is in foreground - app.activate(options: .activateIgnoringOtherApps) + app.activate() try await Task.sleep(nanoseconds: 500_000_000) // 0.5s // Capture with foreground focus - let command = ImageCommand() + _ = ImageCommand() // Set properties as needed // command.app = Self.testHostAppName - // command.captureFocus = .foreground // This would test the actual foreground capture logic print("Test host should now be in foreground") diff --git a/peekaboo-cli/Tests/peekabooTests/ScreenshotValidationTests.swift b/peekaboo-cli/Tests/peekabooTests/ScreenshotValidationTests.swift index a069644..841248d 100644 --- a/peekaboo-cli/Tests/peekabooTests/ScreenshotValidationTests.swift +++ b/peekaboo-cli/Tests/peekabooTests/ScreenshotValidationTests.swift @@ -1,5 +1,6 @@ import AppKit import CoreGraphics +import ScreenCaptureKit @testable import peekaboo import Testing @@ -26,7 +27,7 @@ struct ScreenshotValidationTests { let outputPath = "/tmp/peekaboo-content-test.png" defer { try? FileManager.default.removeItem(atPath: outputPath) } - let captureData = try captureWindowToFile(windowID: windowID, path: outputPath, format: .png) + _ = try captureWindowToFile(windowID: windowID, path: outputPath, format: .png) // Load and analyze the image guard let image = NSImage(contentsOfFile: outputPath) else { @@ -94,7 +95,7 @@ struct ScreenshotValidationTests { let path = "/tmp/peekaboo-format-test.\(format.rawValue)" defer { try? FileManager.default.removeItem(atPath: path) } - let captureData = try captureWindowToFile(windowID: windowID, path: path, format: format) + _ = try captureWindowToFile(windowID: windowID, path: path, format: format) #expect(FileManager.default.fileExists(atPath: path)) @@ -234,16 +235,9 @@ struct ScreenshotValidationTests { path: String, format: ImageFormat ) throws -> ImageCaptureData { - // Create image from window - guard let image = CGWindowListCreateImage( - .null, - .optionIncludingWindow, - windowID, - [.boundsIgnoreFraming, .nominalResolution] - ) else { - throw CaptureError.windowCaptureFailed - } - + // Use modern ScreenCaptureKit API instead of deprecated CGWindowListCreateImage + let image = try captureWindowWithScreenCaptureKit(windowID: windowID) + // Save to file let nsImage = NSImage(cgImage: image, size: NSSize(width: image.width, height: image.height)) try saveImage(nsImage, to: path, format: format) @@ -259,6 +253,57 @@ struct ScreenshotValidationTests { ) ]) } + + private func captureWindowWithScreenCaptureKit(windowID: CGWindowID) throws -> CGImage { + // This needs to be async, so we'll use a semaphore to make it synchronous for the test + var capturedImage: CGImage? + var captureError: Error? + let semaphore = DispatchSemaphore(value: 0) + + Task { + do { + // Get available content + let availableContent = try await SCShareableContent.current + + // Find the window by ID + guard let scWindow = availableContent.windows.first(where: { $0.windowID == windowID }) else { + throw CaptureError.windowNotFound + } + + // Create content filter for the specific window + let filter = SCContentFilter(desktopIndependentWindow: scWindow) + + // Configure capture settings + let configuration = SCStreamConfiguration() + configuration.backgroundColor = .clear + configuration.shouldBeOpaque = true + configuration.showsCursor = false + + // Capture the image + let image = try await SCScreenshotManager.captureImage( + contentFilter: filter, + configuration: configuration + ) + + capturedImage = image + } catch { + captureError = error + } + semaphore.signal() + } + + semaphore.wait() + + if let error = captureError { + throw error + } + + guard let image = capturedImage else { + throw CaptureError.windowCaptureFailed + } + + return image + } private func captureDisplayToFile( displayID: CGDirectDisplayID,