mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-17 21:31:36 +00:00
* fix(macos): bootstrap Codex before onboarding test * fix(plugins): activate deferred runtimes on demand * fix(macos): stage Codex runtime for onboarding probe * fix(macos): expose sanitized onboarding errors * fix(codex): reuse native CLI auth during onboarding * fix(macos): hide Crestodian without inference * fix(codex): honor explicit runtime policy during setup * fix(onboarding): redact verification errors * fix(macos): reset onboarding state with gateway route * fix(onboarding): persist Codex plugin install metadata * chore: refresh onboarding inventories * fix(macos): restart AI setup after gateway change * chore: refresh native onboarding inventory * fix(onboarding): defer gateway restart until setup persists * fix(macos): reset Crestodian on gateway changes * chore(macos): refresh native i18n inventory * fix(onboarding): harden inference setup state * docs(skills): reuse pristine Parallels snapshots * chore(macos): refresh onboarding i18n inventory * fix(onboarding): prevent stale gateway and install state * docs(skills): preserve saved Parallels sessions * fix(macos): balance AI onboarding layout * fix(parallels): parse npm workspace pack output * chore(macos): refresh onboarding i18n inventory * fix(parallels): bundle workspace runtime in package artifact * fix(parallels): isolate canonical package helper * fix(parallels): pack self-contained workspace artifact * fix(packaging): exclude macOS app bundle * fix(macos): restart inference checks after route changes * docs(changelog): defer onboarding note to release * fix(onboarding): enforce inference-first gateway setup
303 lines
11 KiB
Swift
303 lines
11 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
import Testing
|
|
@testable import OpenClaw
|
|
|
|
@Suite(.serialized)
|
|
@MainActor
|
|
struct SettingsViewSmokeTests {
|
|
@Test func `cron settings builds body`() {
|
|
let store = CronJobsStore(isPreview: true)
|
|
store.schedulerEnabled = false
|
|
store.schedulerStorePath = "/tmp/openclaw-cron-store.json"
|
|
|
|
let job1 = CronJob(
|
|
id: "job-1",
|
|
agentId: "ops",
|
|
name: " Morning Check-in ",
|
|
description: nil,
|
|
enabled: true,
|
|
deleteAfterRun: nil,
|
|
createdAtMs: 1_700_000_000_000,
|
|
updatedAtMs: 1_700_000_100_000,
|
|
schedule: .cron(expr: "0 8 * * *", tz: "UTC"),
|
|
sessionTarget: .main,
|
|
wakeMode: .now,
|
|
payload: .systemEvent(text: "ping"),
|
|
delivery: nil,
|
|
state: CronJobState(
|
|
nextRunAtMs: 1_700_000_200_000,
|
|
runningAtMs: nil,
|
|
lastRunAtMs: 1_700_000_050_000,
|
|
lastStatus: "ok",
|
|
lastError: nil,
|
|
lastDurationMs: 123))
|
|
|
|
let job2 = CronJob(
|
|
id: "job-2",
|
|
agentId: nil,
|
|
name: "",
|
|
description: nil,
|
|
enabled: false,
|
|
deleteAfterRun: nil,
|
|
createdAtMs: 1_700_000_000_000,
|
|
updatedAtMs: 1_700_000_100_000,
|
|
schedule: .every(everyMs: 30000, anchorMs: nil),
|
|
sessionTarget: .isolated,
|
|
wakeMode: .nextHeartbeat,
|
|
payload: .agentTurn(
|
|
message: "hello",
|
|
thinking: "low",
|
|
timeoutSeconds: 30,
|
|
deliver: nil,
|
|
channel: nil,
|
|
to: nil,
|
|
bestEffortDeliver: nil),
|
|
delivery: CronDelivery(mode: .announce, channel: "sms", to: "+15551234567", bestEffort: true),
|
|
state: CronJobState(
|
|
nextRunAtMs: nil,
|
|
runningAtMs: nil,
|
|
lastRunAtMs: nil,
|
|
lastStatus: nil,
|
|
lastError: nil,
|
|
lastDurationMs: nil))
|
|
|
|
store.jobs = [job1, job2]
|
|
store.selectedJobId = job1.id
|
|
store.runEntries = [
|
|
CronRunLogEntry(
|
|
ts: 1_700_000_050_000,
|
|
jobId: job1.id,
|
|
action: "finished",
|
|
status: "ok",
|
|
error: nil,
|
|
summary: "ok",
|
|
runAtMs: 1_700_000_050_000,
|
|
durationMs: 123,
|
|
nextRunAtMs: 1_700_000_200_000),
|
|
]
|
|
|
|
let view = CronSettings(store: store)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `cron settings renders in hosting view`() {
|
|
let store = CronJobsStore(isPreview: true)
|
|
store.schedulerEnabled = false
|
|
store.jobs = [
|
|
CronJob(
|
|
id: "job-1",
|
|
agentId: "ops",
|
|
name: "Morning Check-in",
|
|
description: "Summary job",
|
|
enabled: true,
|
|
deleteAfterRun: nil,
|
|
createdAtMs: 1_700_000_000_000,
|
|
updatedAtMs: 1_700_000_100_000,
|
|
schedule: .cron(expr: "0 8 * * *", tz: "UTC"),
|
|
sessionTarget: .isolated,
|
|
wakeMode: .nextHeartbeat,
|
|
payload: .agentTurn(
|
|
message: "Summarize",
|
|
thinking: "low",
|
|
timeoutSeconds: 120,
|
|
deliver: nil,
|
|
channel: nil,
|
|
to: nil,
|
|
bestEffortDeliver: nil),
|
|
delivery: CronDelivery(mode: .announce, channel: "whatsapp", to: "+15551234567", bestEffort: true),
|
|
state: CronJobState(
|
|
nextRunAtMs: 1_700_000_200_000,
|
|
runningAtMs: nil,
|
|
lastRunAtMs: 1_700_000_050_000,
|
|
lastStatus: "ok",
|
|
lastError: nil,
|
|
lastDurationMs: 1200)),
|
|
]
|
|
store.selectedJobId = "job-1"
|
|
store.runEntries = [
|
|
CronRunLogEntry(
|
|
ts: 1_700_000_050_000,
|
|
jobId: "job-1",
|
|
action: "finished",
|
|
status: "ok",
|
|
error: nil,
|
|
summary: "done",
|
|
runAtMs: 1_700_000_050_000,
|
|
durationMs: 1200,
|
|
nextRunAtMs: 1_700_000_200_000),
|
|
]
|
|
|
|
let view = CronSettings(store: store, channelsStore: ChannelsStore(isPreview: true))
|
|
let hosting = NSHostingView(rootView: view)
|
|
hosting.frame = NSRect(x: 0, y: 0, width: 900, height: 700)
|
|
hosting.layoutSubtreeIfNeeded()
|
|
_ = hosting.fittingSize
|
|
}
|
|
|
|
@Test func `cron settings exercises private views`() {
|
|
CronSettings.exerciseForTesting()
|
|
}
|
|
|
|
@Test func `config settings builds body`() {
|
|
let view = ConfigSettings()
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `debug settings builds body`() {
|
|
let view = DebugSettings()
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `general settings builds body`() {
|
|
let state = AppState(preview: true)
|
|
let view = GeneralSettings(state: state)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `general settings exercises branches`() {
|
|
GeneralSettings.exerciseForTesting()
|
|
}
|
|
|
|
@Test func `sessions settings builds body`() {
|
|
let view = SessionsSettings(rows: SessionRow.previewRows, isPreview: true)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `instances settings builds body`() {
|
|
let store = InstancesStore(isPreview: true)
|
|
store.instances = [
|
|
InstanceInfo(
|
|
id: "local",
|
|
host: "this-mac",
|
|
ip: "127.0.0.1",
|
|
version: "1.0",
|
|
platform: "macos 15.0",
|
|
deviceFamily: "Mac",
|
|
modelIdentifier: "MacPreview",
|
|
lastInputSeconds: 12,
|
|
mode: "local",
|
|
reason: "test",
|
|
text: "test instance",
|
|
ts: Date().timeIntervalSince1970 * 1000),
|
|
]
|
|
let view = InstancesSettings(store: store)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `permissions settings builds body`() {
|
|
let view = PermissionsSettings(
|
|
status: [
|
|
.notifications: true,
|
|
.screenRecording: false,
|
|
],
|
|
refresh: {},
|
|
showOnboarding: {})
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `settings root view builds body`() {
|
|
let state = AppState(preview: true)
|
|
let view = SettingsRootView(state: state, updater: nil, initialTab: .general)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `Crestodian settings require configured inference`() {
|
|
#expect(!CrestodianAvailability.shouldShow(configuredModel: nil))
|
|
#expect(!CrestodianAvailability.shouldShow(configuredModel: " "))
|
|
#expect(CrestodianAvailability.shouldShow(configuredModel: "openai/gpt-5.5"))
|
|
|
|
let hiddenTabs = SettingsTabGroup.defaultGroups(showDebug: false, showCrestodian: false)
|
|
.flatMap(\.tabs)
|
|
let visibleTabs = SettingsTabGroup.defaultGroups(showDebug: false, showCrestodian: true)
|
|
.flatMap(\.tabs)
|
|
#expect(!hiddenTabs.contains(.crestodian))
|
|
#expect(visibleTabs.contains(.crestodian))
|
|
#expect(SettingsRootView.normalizedTab(
|
|
.crestodian,
|
|
showDebug: false,
|
|
showCrestodian: false) == .general)
|
|
#expect(SettingsRootView.normalizedTab(
|
|
.crestodian,
|
|
showDebug: false,
|
|
showCrestodian: true) == .crestodian)
|
|
let loadingSelection = SettingsRootView.tabSelection(
|
|
requested: .crestodian,
|
|
showDebug: false,
|
|
inferenceConfiguration: .loading)
|
|
#expect(loadingSelection.selected == .general)
|
|
#expect(loadingSelection.deferred == .crestodian)
|
|
let configuredSelection = SettingsRootView.tabSelection(
|
|
requested: loadingSelection.deferred ?? .general,
|
|
showDebug: false,
|
|
inferenceConfiguration: .loaded("openai/gpt-5.5"))
|
|
#expect(configuredSelection.selected == .crestodian)
|
|
#expect(configuredSelection.deferred == nil)
|
|
let unconfiguredSelection = SettingsRootView.tabSelection(
|
|
requested: .crestodian,
|
|
showDebug: false,
|
|
inferenceConfiguration: .loaded(nil))
|
|
#expect(unconfiguredSelection.selected == .general)
|
|
#expect(unconfiguredSelection.deferred == nil)
|
|
#expect(SettingsRootView.configurationAfterInferenceRefresh(
|
|
current: .loaded("openai/gpt-5.5"),
|
|
result: .failed) == .loaded("openai/gpt-5.5"))
|
|
#expect(SettingsRootView.configurationAfterInferenceRefresh(
|
|
current: .loaded("openai/gpt-5.5"),
|
|
result: .confirmed(nil)) == .loaded(nil))
|
|
}
|
|
|
|
@Test func `Crestodian preserves same route and resets for gateway changes`() {
|
|
let stateDir = URL(fileURLWithPath: "/Users/tester/.openclaw")
|
|
let directA = MacChatTranscriptCache.gatewayID(
|
|
mode: .remote,
|
|
localStateDir: stateDir,
|
|
remoteTransport: .direct,
|
|
directURL: URL(string: "wss://gateway.example.com/team-a"),
|
|
sshTarget: "",
|
|
sshRemotePort: 18789)
|
|
let directB = MacChatTranscriptCache.gatewayID(
|
|
mode: .remote,
|
|
localStateDir: stateDir,
|
|
remoteTransport: .direct,
|
|
directURL: URL(string: "wss://gateway.example.com/team-b"),
|
|
sshTarget: "",
|
|
sshRemotePort: 18789)
|
|
|
|
#expect(directA != directB)
|
|
#expect(SettingsRootView.configRefreshPlan(
|
|
selectedTab: .crestodian,
|
|
previousGatewayID: directA,
|
|
currentGatewayID: directA) == .init(clearsPrevious: false, resetsCrestodian: false))
|
|
#expect(SettingsRootView.configRefreshPlan(
|
|
selectedTab: .general,
|
|
previousGatewayID: directA,
|
|
currentGatewayID: directA) == .init(clearsPrevious: true, resetsCrestodian: false))
|
|
#expect(SettingsRootView.configRefreshPlan(
|
|
selectedTab: .crestodian,
|
|
previousGatewayID: directA,
|
|
currentGatewayID: directB) == .init(clearsPrevious: true, resetsCrestodian: true))
|
|
}
|
|
|
|
@Test func `about settings builds body`() {
|
|
let view = AboutSettings(updater: nil)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `voice wake settings builds body`() {
|
|
let state = AppState(preview: true)
|
|
let view = VoiceWakeSettings(state: state, isActive: false)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `skills settings builds body`() {
|
|
let view = SkillsSettings(state: .preview)
|
|
_ = view.body
|
|
}
|
|
|
|
@Test func `exec approvals settings builds body`() {
|
|
let view = ExecApprovalsSettings()
|
|
_ = view.body
|
|
}
|
|
}
|