2cc81c4628
Add local clipboard intent recommendations, webpage and phone actions, and safer host handoffs. Refine managed gateway, catalog refresh, onboarding, and adaptive polish behavior.
139 lines
4.9 KiB
Swift
139 lines
4.9 KiB
Swift
// ClipboardSemanticAnalyzerTests.swift
|
|
// OSGKeyboardTests
|
|
|
|
@testable import OSGKeyboardShared
|
|
import XCTest
|
|
|
|
final class ClipboardSemanticAnalyzerTests: XCTestCase {
|
|
func testEmptyTextReturnsNoLabels() async {
|
|
let analysis = await ClipboardSemanticAnalyzer().analyze(" \n ")
|
|
|
|
XCTAssertNil(analysis.language)
|
|
XCTAssertEqual(analysis.sentiment, .unknown)
|
|
XCTAssertFalse(analysis.task.isDetected)
|
|
XCTAssertFalse(analysis.question.isDetected)
|
|
XCTAssertFalse(analysis.invitation.isDetected)
|
|
XCTAssertFalse(analysis.complaint.isDetected)
|
|
XCTAssertFalse(analysis.replyableMessage.isDetected)
|
|
}
|
|
|
|
func testDetectsLanguageAndStructuredDataLocally() async {
|
|
let text = """
|
|
Meet on August 28, 2026 at 3:00 PM at 1 Apple Park Way, Cupertino, CA 95014.
|
|
Call +1 408-996-1010 or visit https://www.apple.com.
|
|
"""
|
|
|
|
let analysis = await ClipboardSemanticAnalyzer().analyze(text)
|
|
|
|
XCTAssertEqual(analysis.language?.identifier, "en")
|
|
XCTAssertTrue(analysis.hasDateOrTime)
|
|
XCTAssertTrue(analysis.hasAddress)
|
|
XCTAssertTrue(analysis.hasPhoneNumber)
|
|
XCTAssertTrue(analysis.hasURL)
|
|
}
|
|
|
|
func testStandaloneHTTPSLinkBecomesSingleWebURL() async {
|
|
let analysis = await ClipboardSemanticAnalyzer().analyze(
|
|
"https://www.apple.com/newsroom/"
|
|
)
|
|
|
|
XCTAssertEqual(
|
|
analysis.singleWebURL?.absoluteString,
|
|
"https://www.apple.com/newsroom/"
|
|
)
|
|
}
|
|
|
|
func testBareDomainIsNormalizedToHTTPS() {
|
|
let url = ClipboardWebLinkResolver.singleWebURL(
|
|
in: "详情见 www.apple.com/newsroom/"
|
|
)
|
|
|
|
XCTAssertEqual(
|
|
url?.absoluteString,
|
|
"https://www.apple.com/newsroom/"
|
|
)
|
|
}
|
|
|
|
func testMultipleLinksHaveNoSingleActionTarget() {
|
|
XCTAssertNil(
|
|
ClipboardWebLinkResolver.singleWebURL(
|
|
in: "https://example.com/a 和 https://example.com/b"
|
|
)
|
|
)
|
|
}
|
|
|
|
func testStandalonePhoneNumberBecomesSingleActionTarget() async throws {
|
|
let analysis = await ClipboardSemanticAnalyzer().analyze(
|
|
"请拨打 +1 (408) 996-1010"
|
|
)
|
|
|
|
XCTAssertEqual(analysis.singlePhoneNumber, "+14089961010")
|
|
XCTAssertEqual(
|
|
AIPhoneNumberResolver.telephoneURL(
|
|
for: try XCTUnwrap(analysis.singlePhoneNumber)
|
|
)?.absoluteString,
|
|
"tel:+14089961010"
|
|
)
|
|
}
|
|
|
|
func testMultiplePhoneNumbersHaveNoSingleActionTarget() {
|
|
let labels = [
|
|
ClipboardTextLabel(sourceText: "+1 408-996-1010"),
|
|
ClipboardTextLabel(sourceText: "400-666-8800")
|
|
]
|
|
XCTAssertNil(AIPhoneNumberResolver.singlePhoneNumber(from: labels))
|
|
}
|
|
|
|
func testApprovedModelsDetectHighConfidenceIntents() async {
|
|
let analyzer = ClipboardSemanticAnalyzer()
|
|
|
|
let task = await analyzer.analyze(
|
|
"请今天下班前发送会议纪要,完成后发给项目群。"
|
|
)
|
|
let question = await analyzer.analyze(
|
|
"退款流程具体是怎么安排的?"
|
|
)
|
|
let invitation = await analyzer.analyze(
|
|
"今晚七点在老地方吃饭,你能来吗?"
|
|
)
|
|
let complaint = await analyzer.analyze(
|
|
"应用一直闪退,数据还丢了,你们能尽快处理吗?"
|
|
)
|
|
|
|
XCTAssertTrue(task.task.isApprovedForAutomaticRouting)
|
|
XCTAssertTrue(task.task.isDetected)
|
|
XCTAssertTrue(question.question.isApprovedForAutomaticRouting)
|
|
XCTAssertTrue(question.question.isDetected)
|
|
XCTAssertTrue(invitation.invitation.isApprovedForAutomaticRouting)
|
|
XCTAssertTrue(invitation.invitation.isDetected)
|
|
XCTAssertTrue(complaint.complaint.isApprovedForAutomaticRouting)
|
|
XCTAssertTrue(complaint.complaint.isDetected)
|
|
}
|
|
|
|
func testReplyableModelDistinguishesConversationFromAcknowledgment() async {
|
|
let analyzer = ClipboardSemanticAnalyzer()
|
|
|
|
let conversation = await analyzer.analyze(
|
|
"我刚到家,今天真是累坏了。"
|
|
)
|
|
let acknowledgment = await analyzer.analyze(
|
|
"收到,谢谢。"
|
|
)
|
|
|
|
XCTAssertTrue(conversation.replyableMessage.isApprovedForAutomaticRouting)
|
|
XCTAssertTrue(conversation.replyableMessage.isDetected)
|
|
XCTAssertTrue(acknowledgment.replyableMessage.isApprovedForAutomaticRouting)
|
|
XCTAssertFalse(acknowledgment.replyableMessage.isDetected)
|
|
}
|
|
|
|
func testPersonalPlanDoesNotBecomeAutomaticTask() async {
|
|
let analysis = await ClipboardSemanticAnalyzer().analyze(
|
|
"私人备忘:我准备周五自己整理完这份报告。"
|
|
)
|
|
|
|
XCTAssertTrue(analysis.task.isApprovedForAutomaticRouting)
|
|
XCTAssertFalse(analysis.task.isDetected)
|
|
XCTAssertFalse(analysis.replyableMessage.isDetected)
|
|
}
|
|
}
|