mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-17 08:02:12 -06:00
1ca6b6ff67
* feat(ios): unify chat and voice experience * refactor(ios): finish unified chat ownership * fix(ios): unify chat and voice capture ownership * fix(ios): serialize Talk permission hydration * fix(ios): clear voice CI regressions * fix(ios): clear remaining unified chat CI failures * chore(i18n): sync native source inventory * refactor(ios): defer provider-only realtime voice * fix(ios): clean unified chat voice PR * fix(ios): localize unified voice controls * fix(ios): serialize composer audio controls * fix(ios): isolate dictation preparation lifecycle * fix(ios): key chat tab icons by appearance * fix(ios): preserve talk upgrade handshakes * fix(ios): refresh native i18n inventory * fix(ios): reconcile shared locale artifacts * fix(ios): satisfy chat view lint limit * fix(ios): reconcile shared composer implementation * test(apple): avoid XCTest actor deinit crash * fix(ios): satisfy SwiftFormat scope spacing * fix(ios): remove duplicate merged helper declarations
107 lines
4.7 KiB
Swift
107 lines
4.7 KiB
Swift
import OpenClawKit
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
struct GatewayStatusBuilderTests {
|
|
@Test func `paused problem keeps error status`() {
|
|
let state = GatewayStatusBuilder.build(
|
|
gatewayServerName: nil,
|
|
lastGatewayProblem: GatewayConnectionProblem(
|
|
kind: .pairingRequired,
|
|
owner: .gateway,
|
|
title: "Pairing required",
|
|
message: "Approve this device before reconnecting.",
|
|
requestId: "req-123",
|
|
retryable: false,
|
|
pauseReconnect: true),
|
|
gatewayStatusText: "Reconnecting…")
|
|
|
|
#expect(state == .error)
|
|
}
|
|
|
|
@Test func `transient problem keeps error status while reconnecting`() {
|
|
let state = GatewayStatusBuilder.build(
|
|
gatewayServerName: nil,
|
|
lastGatewayProblem: GatewayConnectionProblem(
|
|
kind: .timeout,
|
|
owner: .network,
|
|
title: "Connection timed out",
|
|
message: "The gateway did not respond before the connection timed out.",
|
|
retryable: true,
|
|
pauseReconnect: false),
|
|
gatewayStatusText: "Reconnecting…")
|
|
|
|
#expect(state == .error)
|
|
}
|
|
|
|
@Test func `chat gateway status labels match display state`() {
|
|
#expect(ChatProTab.gatewayStatusTitle(state: .disconnected, isGatewayUsable: false) == "Offline")
|
|
#expect(ChatProTab.gatewayStatusTitle(state: .connecting, isGatewayUsable: false) == "Connecting")
|
|
#expect(ChatProTab.gatewayStatusTitle(state: .error, isGatewayUsable: false) == "Attention")
|
|
#expect(ChatProTab.gatewayStatusTitle(state: .connected, isGatewayUsable: true) == "Connected")
|
|
#expect(ChatProTab.gatewayStatusTitle(state: .connected, isGatewayUsable: false) == "Unavailable")
|
|
}
|
|
|
|
@Test func `chat gateway status tones separate healthy issue and offline states`() {
|
|
#expect(ChatProTab.gatewayStatusTone(state: .connected, isGatewayUsable: true) == .success)
|
|
#expect(ChatProTab.gatewayStatusTone(state: .connected, isGatewayUsable: false) == .warning)
|
|
#expect(ChatProTab.gatewayStatusTone(state: .connecting, isGatewayUsable: false) == .warning)
|
|
#expect(ChatProTab.gatewayStatusTone(state: .error, isGatewayUsable: false) == .warning)
|
|
#expect(ChatProTab.gatewayStatusTone(state: .disconnected, isGatewayUsable: false) == .error)
|
|
}
|
|
|
|
@Test func `chat gateway status expands on tap or whenever gateway needs attention`() {
|
|
#expect(!ChatProTab.gatewayStatusShouldExpand(
|
|
state: .connected,
|
|
isGatewayUsable: true,
|
|
isManuallyExpanded: false))
|
|
#expect(ChatProTab.gatewayStatusShouldExpand(
|
|
state: .connected,
|
|
isGatewayUsable: true,
|
|
isManuallyExpanded: true))
|
|
#expect(ChatProTab.gatewayStatusShouldExpand(
|
|
state: .connecting,
|
|
isGatewayUsable: false,
|
|
isManuallyExpanded: false))
|
|
#expect(ChatProTab.gatewayStatusShouldExpand(
|
|
state: .error,
|
|
isGatewayUsable: false,
|
|
isManuallyExpanded: false))
|
|
#expect(ChatProTab.gatewayStatusShouldExpand(
|
|
state: .disconnected,
|
|
isGatewayUsable: false,
|
|
isManuallyExpanded: false))
|
|
}
|
|
|
|
@Test func `chat agent badge rejects placeholder question mark`() {
|
|
#expect(ChatProTab.normalizedBadgeEmoji(" 🦞 ") == "🦞")
|
|
#expect(ChatProTab.normalizedBadgeEmoji("?") == nil)
|
|
#expect(ChatProTab.normalizedBadgeEmoji(" ") == nil)
|
|
#expect(ChatProTab.normalizedBadgeEmoji(nil) == nil)
|
|
#expect(ChatProTab.initialsBadge(for: "Agent Smith") == "AS")
|
|
}
|
|
|
|
@Test func `pinned attachment displays its captured gateway owner`() {
|
|
#expect(ChatProTab.presentationGatewayState(
|
|
current: .connected,
|
|
isAttachmentOwnerPinned: true,
|
|
capturedOwnerID: "gateway-a",
|
|
currentOwnerID: "gateway-b") == .disconnected)
|
|
#expect(ChatProTab.presentationGatewayState(
|
|
current: .connected,
|
|
isAttachmentOwnerPinned: true,
|
|
capturedOwnerID: "gateway-a",
|
|
currentOwnerID: "gateway-a") == .connected)
|
|
#expect(ChatProTab.presentationGatewayState(
|
|
current: .connected,
|
|
isAttachmentOwnerPinned: false,
|
|
capturedOwnerID: "gateway-a",
|
|
currentOwnerID: "gateway-b") == .connected)
|
|
}
|
|
|
|
@Test func `chat starter prompts stay stable and actionable`() {
|
|
#expect(ChatProTab.emptyAssistantPrompts.map(\.id) == ["summarize-status", "show-controls", "start-voice"])
|
|
#expect(ChatProTab.emptyAssistantPrompts.allSatisfy { !$0.title.isEmpty && !$0.prompt.isEmpty })
|
|
}
|
|
}
|