ios: unify attachment controls and message typography
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import XCTest
|
||||
import UIKit
|
||||
|
||||
/// Actual normal chat and Agent views, using only the loopback contract fixture.
|
||||
@MainActor final class AgentPolishUITests: XCTestCase {
|
||||
let app = XCUIApplication()
|
||||
override func setUp() async throws {
|
||||
continueAfterFailure = false
|
||||
XCUIDevice.shared.orientation = .portrait
|
||||
try await control(["action": "reset", "count": 0])
|
||||
app.launchArguments = ["-sybil.ios.apiBaseURL", "http://127.0.0.1:8799/api", "-sybil.ios.adminToken", "fixture-token"]
|
||||
}
|
||||
override func tearDown() async throws { app.terminate() }
|
||||
private func control(_ body: [String: Any]) async throws {
|
||||
var request = URLRequest(url: URL(string: "http://127.0.0.1:8799/control")!)
|
||||
request.httpMethod = "POST"; request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
let (_, response) = try await URLSession.shared.data(for: request)
|
||||
XCTAssertEqual((response as? HTTPURLResponse)?.statusCode, 200)
|
||||
}
|
||||
private func sidebar() {
|
||||
if !app.buttons["agent-sidebar"].isHittable {
|
||||
XCTAssertTrue(app.buttons["Show sidebar"].waitForExistence(timeout: 15))
|
||||
app.buttons["Show sidebar"].tap()
|
||||
}
|
||||
}
|
||||
private func openChat() {
|
||||
sidebar()
|
||||
let chat = app.buttons.containing(NSPredicate(format: "label CONTAINS %@", "Transcript fade comparison")).firstMatch
|
||||
XCTAssertTrue(chat.waitForExistence(timeout: 8)); chat.tap()
|
||||
XCTAssertTrue(app.buttons["chat-attach"].waitForExistence(timeout: 8))
|
||||
}
|
||||
private func openAgent() {
|
||||
sidebar(); app.buttons["agent-sidebar"].tap()
|
||||
XCTAssertTrue(app.buttons["agent-attach"].waitForExistence(timeout: 8))
|
||||
}
|
||||
private func capture(_ name: String) {
|
||||
let shot = XCTAttachment(screenshot: XCUIScreen.main.screenshot())
|
||||
shot.name = name; shot.lifetime = .keepAlways; add(shot)
|
||||
}
|
||||
func testSharedAttachmentControlAndPickerFlows() async throws {
|
||||
try await control(["action": "transcript-comparison"])
|
||||
app.launch(); openChat()
|
||||
let normal = app.buttons["chat-attach"]
|
||||
XCTAssertEqual(normal.label, "Attach file"); XCTAssertTrue(normal.isEnabled)
|
||||
let normalSize = normal.frame.size
|
||||
let normalLabel = normal.label
|
||||
XCTAssertGreaterThanOrEqual(normalSize.width, 44); XCTAssertGreaterThanOrEqual(normalSize.height, 44)
|
||||
capture("polish-normal-attachment-control")
|
||||
normal.tap(); app.buttons["Files"].tap()
|
||||
XCTAssertTrue(app.buttons["Cancel"].firstMatch.waitForExistence(timeout: 5)); app.buttons["Cancel"].firstMatch.tap()
|
||||
normal.tap(); app.buttons["Photo Library"].tap()
|
||||
XCTAssertTrue(app.buttons["Cancel"].firstMatch.waitForExistence(timeout: 5)); app.buttons["Cancel"].firstMatch.tap()
|
||||
openAgent()
|
||||
let agent = app.buttons["agent-attach"]
|
||||
XCTAssertEqual(agent.label, normalLabel); XCTAssertTrue(agent.isEnabled)
|
||||
XCTAssertEqual(agent.frame.size.width, normalSize.width, accuracy: 1)
|
||||
XCTAssertEqual(agent.frame.size.height, normalSize.height, accuracy: 1)
|
||||
// Same row as the prompt, including the minimum VoiceOver/touch target.
|
||||
XCTAssertEqual(agent.frame.maxY, app.buttons["agent-send"].frame.maxY, accuracy: 1)
|
||||
capture("polish-agent-attachment-control")
|
||||
agent.tap(); app.buttons["Files"].tap()
|
||||
XCTAssertTrue(app.buttons["Cancel"].firstMatch.waitForExistence(timeout: 5)); app.buttons["Cancel"].firstMatch.tap()
|
||||
agent.tap(); app.buttons["Photo Library"].tap()
|
||||
XCTAssertTrue(app.buttons["Cancel"].firstMatch.waitForExistence(timeout: 5)); app.buttons["Cancel"].firstMatch.tap()
|
||||
}
|
||||
|
||||
func testUserAssistantMultilineParagraphMetricsInBothWorkspaces() async throws {
|
||||
try await compareMetrics(user: "User first\nUser second\n\nUser paragraph",
|
||||
peer: "Peer first \nPeer second\n\nPeer paragraph", name: "multiline-paragraphs")
|
||||
}
|
||||
func testPastedParagraphMetricsInBothWorkspaces() async throws {
|
||||
try await compareMetrics(user: "User first\r\nUser second\r\n \r\nUser paragraph",
|
||||
peer: "Peer first \nPeer second\n\t\nPeer paragraph", name: "pasted-paragraphs")
|
||||
}
|
||||
private func compareMetrics(user: String, peer: String, name: String) async throws {
|
||||
try await control(["action": "presentation-samples", "messages": [
|
||||
["role": "user", "content": user], ["role": "assistant", "content": peer]]])
|
||||
app.launch(); openChat()
|
||||
let normal = try metrics()
|
||||
capture("polish-normal-" + name)
|
||||
openAgent()
|
||||
let agent = try metrics()
|
||||
XCTAssertEqual(normal.0, agent.0, accuracy: 1)
|
||||
XCTAssertEqual(normal.1, agent.1, accuracy: 1)
|
||||
capture("polish-agent-" + name)
|
||||
}
|
||||
private func metrics() throws -> (CGFloat, CGFloat) {
|
||||
let user = app.staticTexts["User first\nUser second"]
|
||||
let peer = app.staticTexts["Peer first\nPeer second"]
|
||||
XCTAssertTrue(user.waitForExistence(timeout: 8)); XCTAssertTrue(peer.waitForExistence(timeout: 8))
|
||||
let userParagraph = app.staticTexts["User paragraph"], peerParagraph = app.staticTexts["Peer paragraph"]
|
||||
XCTAssertTrue(userParagraph.exists); XCTAssertTrue(peerParagraph.exists)
|
||||
XCTAssertEqual(user.frame.height, peer.frame.height, accuracy: 1)
|
||||
let gap = userParagraph.frame.minY - user.frame.maxY
|
||||
XCTAssertEqual(gap, peerParagraph.frame.minY - peer.frame.maxY, accuracy: 1)
|
||||
XCTAssertGreaterThan(gap, 0)
|
||||
return (user.frame.height, gap)
|
||||
}
|
||||
func testLiteralUserAndSemanticAssistantMarkdown() async throws {
|
||||
try await control(["action": "presentation-samples", "messages": [
|
||||
["role": "user", "content": "**literal bold** `literal code`\n- literal item\n[not a link](https://example.invalid)"],
|
||||
["role": "assistant", "content": "Soft break\ncontinues here.\n\n**Real bold** and [Local link](http://127.0.0.1:8799).\n\n- First item\n- Second item\n\n```swift\nlet value = 42\n```"]]])
|
||||
app.launch(); openChat()
|
||||
assertMarkdownSemantics(); capture("polish-normal-literal-and-markdown")
|
||||
openAgent()
|
||||
assertMarkdownSemantics(); capture("polish-agent-literal-and-markdown")
|
||||
try await control(["action": "long-tool"])
|
||||
XCTAssertFalse(app.staticTexts["Local tool output"].exists)
|
||||
}
|
||||
private func assertMarkdownSemantics() {
|
||||
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", "**literal bold**")).firstMatch.waitForExistence(timeout: 8))
|
||||
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", "[not a link](https://example.invalid)")).firstMatch.exists)
|
||||
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", "First item")).firstMatch.exists)
|
||||
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS %@", "let value = 42")).firstMatch.exists)
|
||||
XCTAssertTrue(app.links["Local link"].exists)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user