Skip to main content
Glama
window-capture-by-title.test.ts10.6 kB
/** * Unit tests for window capture by title pattern * Tests Requirements 2.3: Search windows by title pattern, capture first matching window, handle no matches gracefully */ import { LinuxCaptureEngine } from "./linux-capture-engine"; import { MacOSCaptureEngine } from "./macos-capture-engine"; import { WindowsCaptureEngine } from "./windows-capture-engine"; import { WindowInfo } from "../types"; import { BaseCaptureEngine } from "./base-capture-engine"; describe("Window Capture by Title Pattern", () => { describe("getWindowByTitle - Pattern Matching", () => { let engine: BaseCaptureEngine; const mockWindows: WindowInfo[] = [ { id: "1", title: "Google Chrome - New Tab", processName: "chrome", pid: 1001, bounds: { x: 0, y: 0, width: 1200, height: 800 }, isMinimized: false, }, { id: "2", title: "Visual Studio Code - main.ts", processName: "code", pid: 1002, bounds: { x: 100, y: 100, width: 1000, height: 700 }, isMinimized: false, }, { id: "3", title: "Terminal - bash", processName: "terminal", pid: 1003, bounds: { x: 200, y: 200, width: 800, height: 600 }, isMinimized: false, }, { id: "4", title: "Firefox Browser", processName: "firefox", pid: 1004, bounds: { x: 300, y: 300, width: 1100, height: 750 }, isMinimized: false, }, ]; beforeEach(() => { // Use Linux engine for testing (same logic applies to all platforms) engine = new LinuxCaptureEngine(); jest.spyOn(engine, "getWindows").mockResolvedValue(mockWindows); }); afterEach(() => { jest.restoreAllMocks(); }); it("should find window by exact title match", async () => { const window = await engine.getWindowByTitle("Terminal - bash"); expect(window).not.toBeNull(); expect(window?.id).toBe("3"); expect(window?.title).toBe("Terminal - bash"); }); it("should find window by partial title match (case-insensitive)", async () => { const window = await engine.getWindowByTitle("chrome"); expect(window).not.toBeNull(); expect(window?.id).toBe("1"); expect(window?.title).toContain("Chrome"); }); it("should find window by regex pattern", async () => { const window = await engine.getWindowByTitle("Visual.*Code"); expect(window).not.toBeNull(); expect(window?.id).toBe("2"); expect(window?.processName).toBe("code"); }); it("should return first matching window when multiple matches exist", async () => { // Both Chrome and Firefox contain "o" but Chrome comes first const window = await engine.getWindowByTitle("o"); expect(window).not.toBeNull(); expect(window?.id).toBe("1"); // First match (Chrome) }); it("should return null when no window matches the pattern", async () => { const window = await engine.getWindowByTitle("NonExistentWindow"); expect(window).toBeNull(); }); it("should handle empty title pattern gracefully", async () => { const window = await engine.getWindowByTitle(""); // Empty pattern should match all windows, return first expect(window).not.toBeNull(); expect(window?.id).toBe("1"); }); it("should handle special regex characters in pattern", async () => { const mockWindowsWithSpecialChars: WindowInfo[] = [ { id: "1", title: "File [modified] - editor.txt", processName: "editor", pid: 2001, bounds: { x: 0, y: 0, width: 800, height: 600 }, isMinimized: false, }, ]; jest .spyOn(engine, "getWindows") .mockResolvedValue(mockWindowsWithSpecialChars); // Should escape special regex characters const window = await engine.getWindowByTitle("\\[modified\\]"); expect(window).not.toBeNull(); expect(window?.title).toContain("[modified]"); }); it("should be case-insensitive by default", async () => { const windowLower = await engine.getWindowByTitle("terminal"); const windowUpper = await engine.getWindowByTitle("TERMINAL"); const windowMixed = await engine.getWindowByTitle("TeRmInAl"); expect(windowLower).not.toBeNull(); expect(windowUpper).not.toBeNull(); expect(windowMixed).not.toBeNull(); expect(windowLower?.id).toBe("3"); expect(windowUpper?.id).toBe("3"); expect(windowMixed?.id).toBe("3"); }); }); describe("Capture Window by Title Pattern - Integration", () => { let engine: BaseCaptureEngine; const mockWindow: WindowInfo = { id: "test-123", title: "Test Application Window", processName: "testapp", pid: 5000, bounds: { x: 100, y: 100, width: 800, height: 600 }, isMinimized: false, }; beforeEach(() => { engine = new LinuxCaptureEngine(); }); afterEach(() => { jest.restoreAllMocks(); }); it("should capture window found by title pattern", async () => { // Mock getWindowByTitle to return a valid window jest.spyOn(engine, "getWindowByTitle").mockResolvedValue(mockWindow); jest.spyOn(engine, "getWindowById").mockResolvedValue(mockWindow); // Mock the actual capture to avoid system calls jest .spyOn(engine as any, "captureWindowX11") .mockResolvedValue(Buffer.from("fake-image-data")); jest .spyOn(engine as any, "captureWindowWayland") .mockResolvedValue(Buffer.from("fake-image-data")); // Find window by title const window = await engine.getWindowByTitle("Test Application"); expect(window).not.toBeNull(); // Capture the found window if (window) { const buffer = await engine.captureWindow(window.id, false); expect(buffer).toBeInstanceOf(Buffer); expect(buffer.length).toBeGreaterThan(0); } }); it("should handle gracefully when no window matches title pattern", async () => { // Mock getWindowByTitle to return null (no match) jest.spyOn(engine, "getWindowByTitle").mockResolvedValue(null); // Find window by title const window = await engine.getWindowByTitle("NonExistentApp"); // Should return null, not throw an error expect(window).toBeNull(); // Attempting to capture should not happen (graceful handling) // In real usage, the caller would check for null before capturing }); it("should handle minimized windows found by title pattern", async () => { const minimizedWindow: WindowInfo = { ...mockWindow, isMinimized: true, }; // Mock getWindowByTitle to return a minimized window jest.spyOn(engine, "getWindowByTitle").mockResolvedValue(minimizedWindow); jest.spyOn(engine, "getWindowById").mockResolvedValue(minimizedWindow); // Find window by title const window = await engine.getWindowByTitle("Test Application"); expect(window).not.toBeNull(); expect(window?.isMinimized).toBe(true); // Attempting to capture should throw an error if (window) { await expect(engine.captureWindow(window.id, false)).rejects.toThrow( "Cannot capture minimized window" ); } }); }); describe("Cross-Platform Consistency", () => { const mockWindows: WindowInfo[] = [ { id: "win1", title: "Test Window", processName: "test", pid: 1000, bounds: { x: 0, y: 0, width: 800, height: 600 }, isMinimized: false, }, ]; it("should work consistently on Linux", async () => { const engine = new LinuxCaptureEngine(); jest.spyOn(engine, "getWindows").mockResolvedValue(mockWindows); const window = await engine.getWindowByTitle("Test"); expect(window).not.toBeNull(); expect(window?.title).toBe("Test Window"); }); it("should work consistently on macOS", async () => { const engine = new MacOSCaptureEngine(); jest.spyOn(engine, "getWindows").mockResolvedValue(mockWindows); const window = await engine.getWindowByTitle("Test"); expect(window).not.toBeNull(); expect(window?.title).toBe("Test Window"); }); it("should work consistently on Windows", async () => { const engine = new WindowsCaptureEngine(); jest.spyOn(engine, "getWindows").mockResolvedValue(mockWindows); const window = await engine.getWindowByTitle("Test"); expect(window).not.toBeNull(); expect(window?.title).toBe("Test Window"); }); }); describe("Edge Cases", () => { let engine: BaseCaptureEngine; beforeEach(() => { engine = new LinuxCaptureEngine(); }); afterEach(() => { jest.restoreAllMocks(); }); it("should handle empty window list", async () => { jest.spyOn(engine, "getWindows").mockResolvedValue([]); const window = await engine.getWindowByTitle("AnyPattern"); expect(window).toBeNull(); }); it("should handle windows with empty titles", async () => { const windowsWithEmptyTitle: WindowInfo[] = [ { id: "1", title: "", processName: "app", pid: 1000, bounds: { x: 0, y: 0, width: 800, height: 600 }, isMinimized: false, }, { id: "2", title: "Normal Window", processName: "app2", pid: 1001, bounds: { x: 100, y: 100, width: 800, height: 600 }, isMinimized: false, }, ]; jest.spyOn(engine, "getWindows").mockResolvedValue(windowsWithEmptyTitle); // Should skip empty title and find the normal window const window = await engine.getWindowByTitle("Normal"); expect(window).not.toBeNull(); expect(window?.id).toBe("2"); }); it("should handle unicode characters in window titles", async () => { const unicodeWindows: WindowInfo[] = [ { id: "1", title: "文档编辑器 - Document.txt", processName: "editor", pid: 1000, bounds: { x: 0, y: 0, width: 800, height: 600 }, isMinimized: false, }, ]; jest.spyOn(engine, "getWindows").mockResolvedValue(unicodeWindows); const window = await engine.getWindowByTitle("文档"); expect(window).not.toBeNull(); expect(window?.title).toContain("文档编辑器"); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Digital-Defiance/mcp-screenshot'

If you have feedback or need assistance with the MCP directory API, please join our Discord server