Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI

on:
push:
branches: [ master, main ]
pull_request:
branches: [ master, main ]

jobs:
test:
name: Test
runs-on: macos-15

steps:
- uses: actions/checkout@v4

- name: Show Swift version
run: swift --version

- name: Build Debug
run: swift build -v

- name: Run Tests
run: swift test -v

build-release:
name: Build Release
runs-on: macos-15

steps:
- uses: actions/checkout@v4

- name: Show Swift version
run: swift --version

- name: Build Release
run: swift build -c release -v

- name: Test Installation
run: |
swift package experimental-install
# Verify the executable exists
test -f ~/.swiftpm/bin/swift-mcp-gui
24 changes: 0 additions & 24 deletions .github/workflows/swift-build.yml

This file was deleted.

213 changes: 189 additions & 24 deletions Tests/swift-mcp-guiTests/Tools/KeyboardToolsTests.swift
Original file line number Diff line number Diff line change
@@ -1,37 +1,202 @@
import XCTest
import Testing
import MCP
@testable import swift_mcp_gui

final class KeyboardToolsTests: XCTestCase {
@Suite("Keyboard Tools Tests")
struct KeyboardToolsTests {
let toolRegistry: ToolRegistry

func testSendKeysToolExecution() async throws {
let tool = SendKeysTool()
let arguments: JSONValue = [
"keys": ["cmd", "c"]
]
init() {
self.toolRegistry = ToolRegistry()
SendKeysTool.register(in: toolRegistry)
}

@Test("Send keys tool execution with command+c")
func sendKeysToolExecution() async throws {
let arguments: Value = .object([
"keys": .array([.string("cmd"), .string("c")])
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: cmd+c"
}
return false
} != nil)
}

@Test("Send keys tool with single key")
func sendKeysToolSingleKey() async throws {
let arguments: Value = .object([
"keys": .array([.string("space")])
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: space"
}
return false
} != nil)
}

@Test("Send keys tool with complex combination")
func sendKeysToolComplexCombination() async throws {
let arguments: Value = .object([
"keys": .array([.string("cmd"), .string("shift"), .string("a")])
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: cmd+shift+a"
}
return false
} != nil)
}

@Test("Send keys tool with alternative key names", arguments: [
(["command"], "command"),
(["ctrl"], "ctrl"),
(["opt"], "opt"),
(["alt"], "alt"),
(["return"], "return"),
(["enter"], "enter"),
(["esc"], "esc"),
(["del"], "del")
])
func sendKeysToolAlternativeNames(keys: [String], displayName: String) async throws {
let arguments: Value = .object([
"keys": .array(keys.map { .string($0) })
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: \(displayName)"
}
return false
} != nil)
}

@Test("Send keys tool with empty array")
func sendKeysToolEmptyArray() async throws {
let arguments: Value = .object([
"keys": .array([])
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == true)
#expect(result.content.first {
if case .text(let text) = $0 {
return text.contains("No keys specified")
}
return false
} != nil)
}

@Test("Send keys tool with missing parameter")
func sendKeysToolMissingParameter() async throws {
let arguments: Value = .object([:])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == true)
#expect(result.content.first {
if case .text(let text) = $0 {
return text.contains("Missing parameter: keys")
}
return false
} != nil)
}

@Test("Send keys tool with invalid key")
func sendKeysToolInvalidKey() async throws {
let arguments: Value = .object([
"keys": .array([.string("invalid_key")])
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == true)
#expect(result.content.first {
if case .text(let text) = $0 {
return text.contains("Unknown key: invalid_key")
}
return false
} != nil)
}

@Test("Send keys tool with wrong parameter type")
func sendKeysToolWrongParameterType() async throws {
let arguments: Value = .object([
"keys": .string("cmd+c") // Should be array, not string
])

let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == true)
#expect(result.content.first {
if case .text(let text) = $0 {
return text.contains("Invalid parameter keys")
}
return false
} != nil)
}

@Test("Send keys tool with number keys", arguments: [
"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"
])
func sendKeysToolNumberKeys(key: String) async throws {
let arguments: Value = .object([
"keys": .array([.string(key)])
])

let result = try await tool.execute(arguments: arguments)
XCTAssertFalse(result.isError)
XCTAssertTrue(result.content.first?.text?.contains("Send key shortcut") ?? false)
let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: \(key)"
}
return false
} != nil)
}

func testSendKeysToolEmptyArray() async throws {
let tool = SendKeysTool()
let arguments: JSONValue = [
"keys": []
]
@Test("Send keys tool with letter keys", arguments: [
"a", "c", "v", "x", "z"
])
func sendKeysToolLetterKeys(key: String) async throws {
let arguments: Value = .object([
"keys": .array([.string(key)])
])

let result = try await tool.execute(arguments: arguments)
XCTAssertTrue(result.isError)
XCTAssertTrue(result.content.first?.text?.contains("Keys array cannot be empty") ?? false)
let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: \(key)"
}
return false
} != nil)
}

func testSendKeysToolMissingParameter() async throws {
let tool = SendKeysTool()
let arguments: JSONValue = [:]
@Test("Send keys tool with arrow keys", arguments: [
"up", "down", "left", "right"
])
func sendKeysToolArrowKeys(key: String) async throws {
let arguments: Value = .object([
"keys": .array([.string(key)])
])

let result = try await tool.execute(arguments: arguments)
XCTAssertTrue(result.isError)
XCTAssertTrue(result.content.first?.text?.contains("Missing parameter: keys") ?? false)
let result = try await toolRegistry.execute(name: "sendKeys", arguments: arguments)
#expect(result.isError == false)
#expect(result.content.first {
if case .text(let text) = $0 {
return text == "Sent key combination: \(key)"
}
return false
} != nil)
}
}
Loading