Files
openclaw/apps/ios/Tests/GatewayStatusBuilderTests.swift
Colin Johnson 1ca6b6ff67 feat(ios): unify chat and voice experience (#107879)
* 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
2026-07-18 14:30:10 -04:00

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 })
}
}