feat(keyboard): add custom skills plus events and navigate Shortcuts
Ship user-defined Shortcut skills, companion Events/Navigate recipes, shared skill/style card chrome, and bump the build to 69.
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
// AIAddressExtractionTests.swift
|
||||
// OSGKeyboardTests
|
||||
|
||||
import XCTest
|
||||
@testable import OSGKeyboardShared
|
||||
|
||||
final class AIAddressExtractionTests: XCTestCase {
|
||||
func testNONEAndEmptyProduceNoItems() {
|
||||
XCTAssertEqual(AIAddressExtraction.lines(from: "NONE"), [])
|
||||
XCTAssertEqual(AIAddressExtraction.lines(from: "没有地址"), [])
|
||||
XCTAssertEqual(AIAddressExtraction.lines(from: "no address"), [])
|
||||
XCTAssertEqual(AIAddressExtraction.lines(from: " \n "), [])
|
||||
}
|
||||
|
||||
func testDestinationOnlyKeepsLeadingPipe() {
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "|三里屯太古里"),
|
||||
["|三里屯太古里"]
|
||||
)
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "朝阳区酒仙桥路10号"),
|
||||
["|朝阳区酒仙桥路10号"]
|
||||
)
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "|三里屯|"),
|
||||
["|三里屯"]
|
||||
)
|
||||
}
|
||||
|
||||
func testOriginAndDestination() {
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "北京南站|三里屯太古里"),
|
||||
["北京南站|三里屯太古里"]
|
||||
)
|
||||
}
|
||||
|
||||
func testFirstValidLineOnly() {
|
||||
let raw = """
|
||||
NONE
|
||||
北京南站|三里屯
|
||||
国贸|望京
|
||||
"""
|
||||
XCTAssertEqual(AIAddressExtraction.lines(from: raw), ["北京南站|三里屯"])
|
||||
}
|
||||
|
||||
func testSameOriginAndDestinationDropsOrigin() {
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "故宫|故宫"),
|
||||
["|故宫"]
|
||||
)
|
||||
}
|
||||
|
||||
func testRejectsURLDestinations() {
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "|https://maps.apple.com/?daddr=x"),
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
func testStripsBullet() {
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: "- |三里屯"),
|
||||
["|三里屯"]
|
||||
)
|
||||
}
|
||||
|
||||
func testWholeClipboardEchoIsRejected() {
|
||||
let source = String(repeating: "这是一段很长的会议纪要内容,包含许多句子。", count: 4)
|
||||
XCTAssertEqual(
|
||||
AIAddressExtraction.lines(from: source, sourceClipboard: source),
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
func testPromptIncludesPipeContractAndNONE() {
|
||||
let prompt = AIClipboardSkillCatalog.instruction(
|
||||
skillID: AIClipboardSkillCatalog.navigateID,
|
||||
locale: "zh",
|
||||
translationTargetLocaleId: TranslationLanguageCatalog.offLocaleId
|
||||
)
|
||||
XCTAssertTrue(prompt.contains("起点|终点"))
|
||||
XCTAssertTrue(prompt.contains("NONE"))
|
||||
XCTAssertTrue(prompt.contains("不要把整段原文当成一个地点"))
|
||||
}
|
||||
|
||||
func testEnglishPromptIncludesNONE() {
|
||||
let prompt = AIClipboardSkillCatalog.instruction(
|
||||
skillID: AIClipboardSkillCatalog.navigateID,
|
||||
locale: "en",
|
||||
translationTargetLocaleId: TranslationLanguageCatalog.offLocaleId
|
||||
)
|
||||
XCTAssertTrue(prompt.contains("origin|destination"))
|
||||
XCTAssertTrue(prompt.contains("NONE"))
|
||||
}
|
||||
}
|
||||
|
||||
final class AIMapNavigationTests: XCTestCase {
|
||||
private let destinationOnly = AIMapRoute(origin: nil, destination: "三里屯太古里")
|
||||
private let twoPoint = AIMapRoute(origin: "北京南站", destination: "三里屯太古里")
|
||||
|
||||
func testPrefersAmapWhenIosamapOpens() {
|
||||
let url = AIMapNavigation.url(for: destinationOnly) { $0.scheme == "iosamap" }
|
||||
XCTAssertEqual(url.scheme, "iosamap")
|
||||
XCTAssertEqual(url.host, "path")
|
||||
let items = query(url)
|
||||
XCTAssertEqual(items["sourceApplication"], "OSGKeyboard")
|
||||
XCTAssertEqual(items["dname"], "三里屯太古里")
|
||||
XCTAssertEqual(items["sname"], "我的位置")
|
||||
XCTAssertEqual(items["t"], "0")
|
||||
}
|
||||
|
||||
func testAmapUsesAmapuriWhenOnlyAmapuriOpens() {
|
||||
let url = AIMapNavigation.url(for: twoPoint) { $0.scheme == "amapuri" }
|
||||
XCTAssertEqual(url.scheme, "amapuri")
|
||||
XCTAssertEqual(url.host, "route")
|
||||
XCTAssertTrue(url.absoluteString.contains("://route/plan/?"))
|
||||
let items = query(url)
|
||||
XCTAssertEqual(items["sname"], "北京南站")
|
||||
XCTAssertEqual(items["dname"], "三里屯太古里")
|
||||
}
|
||||
|
||||
func testFallsBackToBaiduWhenAmapMissing() {
|
||||
let url = AIMapNavigation.url(for: twoPoint) { $0.scheme == "baidumap" }
|
||||
XCTAssertEqual(url.scheme, "baidumap")
|
||||
XCTAssertEqual(url.host, "map")
|
||||
XCTAssertEqual(url.path, "/direction")
|
||||
let items = query(url)
|
||||
XCTAssertEqual(items["origin"], "name:北京南站")
|
||||
XCTAssertEqual(items["destination"], "name:三里屯太古里")
|
||||
XCTAssertEqual(items["mode"], "driving")
|
||||
}
|
||||
|
||||
func testBaiduOmitsOriginWhenStartingFromHere() {
|
||||
let url = AIMapNavigation.url(for: destinationOnly) { $0.scheme == "baidumap" }
|
||||
let items = query(url)
|
||||
XCTAssertNil(items["origin"])
|
||||
XCTAssertEqual(items["destination"], "name:三里屯太古里")
|
||||
}
|
||||
|
||||
func testFallsBackToAppleMaps() {
|
||||
let url = AIMapNavigation.url(for: twoPoint) { _ in false }
|
||||
XCTAssertEqual(url.scheme, "maps")
|
||||
XCTAssertTrue(url.absoluteString.hasPrefix("maps://"))
|
||||
let items = query(url)
|
||||
XCTAssertEqual(items["saddr"], "北京南站")
|
||||
XCTAssertEqual(items["daddr"], "三里屯太古里")
|
||||
XCTAssertEqual(items["dirflg"], "d")
|
||||
}
|
||||
|
||||
func testAppleOmitsSaddrWhenStartingFromHere() {
|
||||
let url = AIMapNavigation.url(for: destinationOnly) { _ in false }
|
||||
let items = query(url)
|
||||
XCTAssertNil(items["saddr"])
|
||||
XCTAssertEqual(items["daddr"], "三里屯太古里")
|
||||
}
|
||||
|
||||
func testShortcutInputEncodesFirstRoute() {
|
||||
let text = AIMapNavigation.shortcutInput(from: "北京南站|三里屯") { _ in false }
|
||||
XCTAssertEqual(text?.hasPrefix("maps:"), true)
|
||||
XCTAssertTrue(text?.contains("daddr") == true)
|
||||
}
|
||||
|
||||
func testShortcutInputRejectsNONE() {
|
||||
XCTAssertNil(AIMapNavigation.shortcutInput(from: "NONE") { _ in false })
|
||||
}
|
||||
|
||||
func testProviderOrder() {
|
||||
XCTAssertEqual(AIMapNavigation.provider { $0.scheme == "iosamap" }, .amap)
|
||||
XCTAssertEqual(AIMapNavigation.provider { $0.scheme == "baidumap" }, .baidu)
|
||||
XCTAssertEqual(AIMapNavigation.provider { _ in false }, .apple)
|
||||
}
|
||||
|
||||
private func query(_ url: URL) -> [String: String] {
|
||||
let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems ?? []
|
||||
return Dictionary(uniqueKeysWithValues: items.compactMap { item in
|
||||
guard let value = item.value else { return nil }
|
||||
return (item.name, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user