This repository has been archived on 2026-10-04. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
hope-voice-ios/UITests/VoiceUITests.swift
T

149 lines
7.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
/// Real UI interactions with isolated preferences. These cover the client,
/// including offline recovery; they do not claim to verify the live backend.
final class VoiceUITests: XCTestCase {
private var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launchArguments = ["--ui-testing", "--reset-ui-testing"]
}
func testLogoutDoesNotLeakPriorAccountTranscriptOrTasks() {
app.launchArguments.append("--ui-fixtures")
app.launch()
XCTAssertTrue(app.staticTexts["Help me plan a quiet weekend."].waitForExistence(timeout: 10))
app.buttons["Account"].tap()
app.buttons["Sign out"].tap()
XCTAssertTrue(app.secureTextFields["Password"].waitForExistence(timeout: 10))
let username = app.textFields["Username"]
username.tap()
username.typeText("second-user")
app.secureTextFields["Password"].tap()
app.secureTextFields["Password"].typeText("offline-only")
app.buttons["Sign in"].tap()
XCTAssertTrue(app.buttons["Start chatting"].waitForExistence(timeout: 10))
XCTAssertFalse(app.staticTexts["Help me plan a quiet weekend."].exists)
app.tabBars.buttons["Activity"].tap()
XCTAssertFalse(app.staticTexts["Search backend timed out after 30s"].exists)
}
func testWelcomeNavigationAndOfflineRecovery() {
app.launch()
XCTAssertTrue(app.buttons["Start chatting"].waitForExistence(timeout: 10))
XCTAssertTrue(app.staticTexts["Your microphone starts after you connect."].exists)
app.buttons["Type"].tap()
XCTAssertTrue(app.buttons["Start chatting"].exists)
XCTAssertTrue(app.staticTexts["Your microphone stays off."].exists)
app.buttons["Start chatting"].tap()
XCTAssertTrue(app.buttons["Try again"].waitForExistence(timeout: 20))
XCTAssertTrue(app.staticTexts["Couldn’t connect"].exists)
app.buttons["Settings"].tap()
XCTAssertTrue(app.textFields["Server URL"].waitForExistence(timeout: 5))
capture("Offline recovery")
}
func testVoiceSelectionPersistsAndURLValidation() {
app.launch()
app.buttons["Settings"].tap()
app.buttons.matching(NSPredicate(format: "label CONTAINS 'Speaking voice'")).firstMatch.tap()
let voice = app.buttons.matching(NSPredicate(format: "label CONTAINS 'Jenny'")).firstMatch
XCTAssertTrue(voice.waitForExistence(timeout: 5))
voice.tap()
XCTAssertTrue(app.buttons.matching(NSPredicate(format: "label CONTAINS 'Speaking voice' AND label CONTAINS 'Jenny'")).firstMatch.exists)
let server = app.textFields["Server URL"]
server.tap()
let original = server.value as? String ?? ""
server.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: original.count) + "http://example.com")
app.buttons["Change server"].tap()
XCTAssertTrue(app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'must use https'")).firstMatch.waitForExistence(timeout: 5))
let invalid = server.value as? String ?? ""
server.tap()
server.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: invalid.count) + " https://127.0.0.1:1 ")
app.buttons["Change server"].tap()
// A validated address presents an explicit account-switch action rather
// than silently replacing the active authenticated server.
let confirm = app.buttons["Change server and sign out"]
XCTAssertTrue(confirm.waitForExistence(timeout: 5))
// Do not apply the destructive action in this fixture: the assertion
// above proves a validated change cannot bypass the confirmation gate.
}
func testTranscriptClearConfirmationAndActivityDetails() {
app.launchArguments.append("--ui-fixtures")
app.launch()
XCTAssertTrue(app.staticTexts["Help me plan a quiet weekend."].waitForExistence(timeout: 10))
app.buttons["Conversation options"].tap()
app.buttons["Clear conversation"].tap()
app.buttons["Cancel"].tap()
XCTAssertTrue(app.staticTexts["Help me plan a quiet weekend."].exists)
app.buttons["Conversation options"].tap()
app.buttons["Clear conversation"].tap()
app.buttons["Clear conversation"].tap()
XCTAssertTrue(app.buttons["Start chatting"].exists)
XCTAssertFalse(app.staticTexts["Help me plan a quiet weekend."].exists)
app.tabBars.buttons["Activity"].tap()
XCTAssertTrue(app.staticTexts["Needs attention"].waitForExistence(timeout: 5))
XCTAssertTrue(app.staticTexts["Search backend timed out after 30s"].exists)
app.staticTexts["Search backend timed out after 30s"].tap()
XCTAssertTrue(app.staticTexts["Fetching benchmark articles…"].waitForExistence(timeout: 5))
capture("Activity details")
}
func testRestoredTranscriptStaysAtTopUntilLatest() {
app.launchArguments.append("--ui-long-transcript")
app.launch()
let first = app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'Earlier message 1:'")).firstMatch
let last = app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'Earlier message 30:'")).firstMatch
XCTAssertTrue(first.waitForExistence(timeout: 10))
XCTAssertTrue(app.buttons["Latest"].exists)
XCTAssertFalse(last.isHittable)
app.buttons["Latest"].tap()
XCTAssertTrue(last.waitForExistence(timeout: 5))
XCTAssertTrue(last.isHittable)
}
func testLargeTypeNavigation() {
app.launchArguments += ["-UIPreferredContentSizeCategoryName", "UICTContentSizeCategoryAccessibilityXXXL"]
app.launch()
XCTAssertTrue(app.buttons["Start chatting"].waitForExistence(timeout: 10))
XCTAssertTrue(app.buttons["Start chatting"].isHittable)
app.tabBars.buttons["Activity"].tap()
XCTAssertTrue(app.staticTexts["No tasks yet"].waitForExistence(timeout: 15))
app.buttons["Settings"].tap()
XCTAssertTrue(app.navigationBars["Settings"].exists)
capture("Accessibility text size")
}
func testOpalPromptPreparesEditableDraftWithoutConnecting() {
app.launch()
XCTAssertTrue(app.staticTexts["A little room for you,"].waitForExistence(timeout: 10))
app.buttons["Explore an idea"].tap()
let draft = app.textFields["Message Hope"]
XCTAssertTrue(draft.waitForExistence(timeout: 5))
XCTAssertEqual(draft.value as? String, "Help me explore an idea.")
draft.tap()
draft.typeText(String(repeating: XCUIKeyboardKey.delete.rawValue, count: "Help me explore an idea.".count))
XCTAssertTrue(draft.exists)
draft.typeText("My own thought")
XCTAssertEqual(draft.value as? String, "My own thought")
XCTAssertTrue(app.buttons["Start chatting"].exists)
XCTAssertTrue(app.staticTexts["Your microphone stays off."].exists)
capture("Opal editable prompt")
}
private func capture(_ name: String) {
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = name
attachment.lifetime = .keepAlways
add(attachment)
}
}