xworkmate-app/test/features/settings_page_suite.dart
2026-03-26 17:55:38 +08:00

879 lines
26 KiB
Dart
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.

@TestOn('vm')
library;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:xworkmate/app/app_controller.dart';
import 'package:xworkmate/app/ui_feature_manifest.dart';
import 'package:xworkmate/features/settings/settings_page.dart';
import 'package:xworkmate/models/app_models.dart';
import 'package:xworkmate/runtime/desktop_platform_service.dart';
import 'package:xworkmate/runtime/runtime_models.dart';
import 'package:xworkmate/runtime/skill_directory_access.dart';
import '../test_support.dart';
class _DesktopServiceStub implements DesktopPlatformService {
@override
DesktopIntegrationState get state =>
DesktopIntegrationState.fromJson(const <String, dynamic>{
'isSupported': true,
'environment': 'kde',
'mode': 'proxy',
'trayAvailable': true,
'trayEnabled': true,
'autostartEnabled': false,
'networkManagerAvailable': true,
'systemProxy': {
'enabled': true,
'host': '127.0.0.1',
'port': 7890,
'backend': 'kioslaverc',
'lastAppliedMode': 'proxy',
},
'tunnel': {
'available': true,
'connected': false,
'connectionName': 'XWorkmate Tunnel',
'backend': 'nmcli',
'lastError': '',
},
'statusMessage': '',
});
@override
bool get isSupported => state.isSupported;
@override
Future<void> initialize(LinuxDesktopConfig config) async {}
@override
Future<void> syncConfig(LinuxDesktopConfig config) async {}
@override
Future<void> refresh() async {}
@override
Future<void> setMode(VpnMode mode) async {}
@override
Future<void> connectTunnel() async {}
@override
Future<void> disconnectTunnel() async {}
@override
Future<void> setLaunchAtLogin(bool enabled) async {}
@override
void dispose() {}
}
Future<AppController> _createControllerWithSkillAccessService(
WidgetTester tester,
SkillDirectoryAccessService skillDirectoryAccessService,
) async {
final controller = AppController(
store: createIsolatedTestStore(enableSecureStorage: false),
skillDirectoryAccessService: skillDirectoryAccessService,
);
addTearDown(controller.dispose);
await tester.pump(const Duration(milliseconds: 100));
await tester.pumpAndSettle();
return controller;
}
class _FakeSkillDirectoryAccessService implements SkillDirectoryAccessService {
_FakeSkillDirectoryAccessService({
required this.userHomeDirectory,
this.multiDirectoryResponse = const <AuthorizedSkillDirectory>[],
});
final String userHomeDirectory;
final List<AuthorizedSkillDirectory> multiDirectoryResponse;
@override
bool get isSupported => true;
@override
Future<String> resolveUserHomeDirectory() async {
return userHomeDirectory;
}
@override
Future<List<AuthorizedSkillDirectory>> authorizeDirectories({
List<String> suggestedPaths = const <String>[],
}) async {
return multiDirectoryResponse;
}
@override
Future<AuthorizedSkillDirectory?> authorizeDirectory({
String suggestedPath = '',
}) async {
final normalized = normalizeAuthorizedSkillDirectoryPath(suggestedPath);
if (normalized.isEmpty) {
return null;
}
return AuthorizedSkillDirectory(
path: normalized,
bookmark: 'bookmark-${normalized.hashCode}',
);
}
@override
Future<SkillDirectoryAccessHandle?> openDirectory(
AuthorizedSkillDirectory directory,
) async {
final normalized = normalizeAuthorizedSkillDirectoryPath(directory.path);
if (normalized.isEmpty) {
return null;
}
return SkillDirectoryAccessHandle(path: normalized, onClose: () async {});
}
}
void main() {
testWidgets('SettingsPage theme chips update controller theme mode', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('外观'));
await tester.pumpAndSettle();
await tester.tap(find.text('深色'));
await tester.pumpAndSettle();
expect(controller.themeMode, ThemeMode.dark);
await tester.tap(find.text('浅色'));
await tester.pumpAndSettle();
expect(controller.themeMode, ThemeMode.light);
});
testWidgets('SettingsPage hides account access controls by default', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
expect(find.text('账号访问'), findsNothing);
expect(find.text('Account Access'), findsNothing);
expect(find.text('账号本地模式'), findsNothing);
expect(find.text('Account local mode'), findsNothing);
});
testWidgets('SettingsPage can expose account access when feature enabled', (
WidgetTester tester,
) async {
final manifest = UiFeatureManifest.fallback().copyWithFeature(
platform: UiFeaturePlatform.desktop,
module: 'settings',
feature: 'account_access',
enabled: true,
releaseTier: UiFeatureReleaseTier.experimental,
);
final controller = await createTestController(
tester,
uiFeatureManifest: manifest,
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
expect(find.text('账号访问'), findsOneWidget);
expect(find.text('账号本地模式'), findsOneWidget);
});
testWidgets('SettingsPage integration tab exposes unified gateway controls', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
expect(find.text('OpenClaw Gateway'), findsWidgets);
expect(find.text('LLM 接入点'), findsOneWidget);
expect(find.text('ACP 外部接入'), findsOneWidget);
expect(find.text('Vault Server'), findsNothing);
expect(find.byKey(const ValueKey('ai-gateway-url-field')), findsNothing);
expect(find.byKey(const ValueKey('gateway-mode-field')), findsNothing);
expect(find.text('认证诊断'), findsNothing);
expect(find.byKey(const ValueKey('gateway-test-button')), findsOneWidget);
expect(find.byKey(const ValueKey('gateway-save-button')), findsOneWidget);
expect(find.byKey(const ValueKey('gateway-apply-button')), findsOneWidget);
expect(
find.byKey(const ValueKey('gateway-profile-chip-0')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('gateway-profile-chip-1')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('gateway-profile-chip-2')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('gateway-profile-chip-3')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('gateway-profile-chip-4')),
findsOneWidget,
);
expect(
find.descendant(
of: find.byKey(const ValueKey('gateway-profile-chip-2')),
matching: find.text('连接源 1'),
),
findsOneWidget,
);
expect(find.text('自定义连接源 1'), findsNothing);
expect(
find.byKey(const ValueKey('gateway-device-security-card')),
findsOneWidget,
);
});
testWidgets('SettingsPage can expose vault section when feature enabled', (
WidgetTester tester,
) async {
final manifest = UiFeatureManifest.fallback().copyWithFeature(
platform: UiFeaturePlatform.desktop,
module: 'settings',
feature: 'vault_server',
enabled: true,
releaseTier: UiFeatureReleaseTier.experimental,
);
final controller = await createTestController(
tester,
uiFeatureManifest: manifest,
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
expect(find.text('Vault Server'), findsOneWidget);
});
testWidgets('SettingsPage integration tab exposes ACP provider endpoints', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('ACP 外部接入').first);
await tester.pumpAndSettle();
expect(find.text('外部 ACP Server Endpoint'), findsOneWidget);
expect(find.text('Codex'), findsWidgets);
expect(find.text('OpenCode'), findsWidgets);
expect(find.text('Claude'), findsNothing);
expect(find.text('Gemini'), findsNothing);
expect(
find.byKey(const ValueKey('external-acp-provider-add-button')),
findsOneWidget,
);
expect(find.text('添加更多自定义配置'), findsOneWidget);
expect(find.textContaining('ws://127.0.0.1:9001'), findsWidgets);
expect(find.text('标志'), findsNothing);
expect(find.text('Badge'), findsNothing);
expect(
find.byKey(const ValueKey('settings-global-save-button')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('settings-global-apply-button')),
findsOneWidget,
);
});
testWidgets('SettingsPage ACP wizard adds a custom provider card', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('ACP 外部接入').first);
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('external-acp-provider-add-button')),
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('external-acp-wizard-name-field')),
'Lab Agent',
);
await tester.enterText(
find.byKey(const ValueKey('external-acp-wizard-endpoint-field')),
'wss://lab.example.com/acp',
);
await tester.tap(
find.byKey(const ValueKey('external-acp-wizard-confirm-button')),
);
await tester.pumpAndSettle();
expect(find.text('Lab Agent'), findsWidgets);
expect(find.text('wss://lab.example.com/acp'), findsWidgets);
});
testWidgets('SettingsPage skills authorization tab keeps only preset roots', (
WidgetTester tester,
) async {
final controller = await _createControllerWithSkillAccessService(
tester,
_FakeSkillDirectoryAccessService(userHomeDirectory: '/Users/tester'),
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('SKILLS 目录授权'));
await tester.pumpAndSettle();
expect(find.text('~/.agents/skills'), findsOneWidget);
expect(find.text('/Users/tester/.agents/skills'), findsOneWidget);
expect(find.text('~/.codex/skills'), findsOneWidget);
expect(find.text('/Users/tester/.codex/skills'), findsOneWidget);
expect(find.text('~/.workbuddy/skills'), findsOneWidget);
expect(find.text('/Users/tester/.workbuddy/skills'), findsOneWidget);
});
testWidgets('SettingsPage can batch add custom skills directories', (
WidgetTester tester,
) async {
final controller = await _createControllerWithSkillAccessService(
tester,
_FakeSkillDirectoryAccessService(
userHomeDirectory: '/Users/tester',
multiDirectoryResponse: const <AuthorizedSkillDirectory>[
AuthorizedSkillDirectory(
path: '/Users/tester/custom-a',
bookmark: 'bookmark-a',
),
AuthorizedSkillDirectory(
path: '/Users/tester/custom-b',
bookmark: 'bookmark-b',
),
],
),
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('SKILLS 目录授权'));
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-batch-add-button')),
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('skill-directory-path-input')),
'''
paths:
- /Users/tester/custom-a
- "/Users/tester/custom-b"
''',
);
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-direct-add-button')),
);
await tester.pump();
for (
var attempt = 0;
attempt < 10 && controller.authorizedSkillDirectories.length < 2;
attempt += 1
) {
await tester.pump(const Duration(milliseconds: 100));
}
expect(
controller.authorizedSkillDirectories.map((item) => item.path),
containsAll(const <String>[
'/Users/tester/custom-a',
'/Users/tester/custom-b',
]),
);
expect(find.text('custom-a'), findsOneWidget);
expect(find.text('custom-b'), findsOneWidget);
});
testWidgets('SettingsPage skills authorization dialog can use picker flow', (
WidgetTester tester,
) async {
final controller = await _createControllerWithSkillAccessService(
tester,
_FakeSkillDirectoryAccessService(
userHomeDirectory: '/Users/tester',
multiDirectoryResponse: const <AuthorizedSkillDirectory>[
AuthorizedSkillDirectory(
path: '/Users/tester/custom-picker',
bookmark: 'bookmark-picker',
),
],
),
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('SKILLS 目录授权'));
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-batch-add-button')),
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('skill-directory-path-input')),
'/Users/tester/custom-picker',
);
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-picker-button')),
);
await tester.pump();
for (
var attempt = 0;
attempt < 10 && controller.authorizedSkillDirectories.isEmpty;
attempt += 1
) {
await tester.pump(const Duration(milliseconds: 100));
}
expect(
controller.authorizedSkillDirectories.map((item) => item.path),
contains('/Users/tester/custom-picker'),
);
});
testWidgets(
'SettingsPage batch add normalizes pasted SKILL.md paths to skill package directories',
(WidgetTester tester) async {
final controller = await _createControllerWithSkillAccessService(
tester,
_FakeSkillDirectoryAccessService(userHomeDirectory: '/Users/tester'),
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.text('SKILLS 目录授权'));
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-batch-add-button')),
);
await tester.pumpAndSettle();
await tester.enterText(
find.byKey(const ValueKey('skill-directory-path-input')),
'/Users/tester/workspaces/ai-workflow-craft/skills/docx/SKILL.md',
);
await tester.pumpAndSettle();
await tester.tap(
find.byKey(const ValueKey('skill-directory-direct-add-button')),
);
await tester.pump();
for (
var attempt = 0;
attempt < 10 && controller.authorizedSkillDirectories.isEmpty;
attempt += 1
) {
await tester.pump(const Duration(milliseconds: 100));
}
expect(
controller.authorizedSkillDirectories.map((item) => item.path),
const <String>[
'/Users/tester/workspaces/ai-workflow-craft/skills/docx',
],
);
expect(find.text('docx'), findsOneWidget);
},
);
testWidgets('SettingsPage gateway sections can collapse individually', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('折叠').first);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('gateway-host-field')), findsNothing);
expect(find.byKey(const ValueKey('gateway-test-button')), findsNothing);
expect(
find.byKey(const ValueKey('gateway-device-security-card')),
findsNothing,
);
await tester.tap(find.byTooltip('展开').first);
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('gateway-host-field')), findsOneWidget);
expect(find.byKey(const ValueKey('gateway-test-button')), findsOneWidget);
expect(
find.byKey(const ValueKey('gateway-device-security-card')),
findsOneWidget,
);
});
testWidgets('SettingsPage shows Linux desktop integration controls', (
WidgetTester tester,
) async {
final controller = await createTestController(
tester,
desktopPlatformService: _DesktopServiceStub(),
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
expect(
find.byKey(const ValueKey('linux-desktop-integration-card')),
findsOneWidget,
);
expect(find.text('Linux 桌面集成'), findsOneWidget);
expect(find.text('切换到代理'), findsOneWidget);
expect(find.text('连接隧道'), findsOneWidget);
});
testWidgets('SettingsPage multi-agent tab keeps header readable', (
WidgetTester tester,
) async {
final manifest = UiFeatureManifest.fallback().copyWithFeature(
platform: UiFeaturePlatform.desktop,
module: 'settings',
feature: 'agents',
enabled: true,
releaseTier: UiFeatureReleaseTier.stable,
);
final controller = await createTestController(
tester,
uiFeatureManifest: manifest,
);
await pumpPage(
tester,
child: const SizedBox(width: 1100, height: 900, child: Placeholder()),
platform: TargetPlatform.macOS,
);
await pumpPage(
tester,
child: SizedBox(
width: 1100,
height: 900,
child: SettingsPage(controller: controller),
),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('多 Agent'));
await tester.pumpAndSettle();
final titleFinder = find.text('多 Agent 协作');
expect(titleFinder, findsOneWidget);
expect(tester.getSize(titleFinder).width, greaterThan(80));
expect(find.text('启用协作模式'), findsOneWidget);
expect(find.text('协作框架'), findsOneWidget);
expect(find.textContaining('Lead Engineer'), findsWidgets);
expect(find.textContaining('ollama launch codex'), findsOneWidget);
expect(tester.takeException(), isNull);
});
testWidgets('SettingsPage hides gateway setup code editor by default', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
await tester.tap(find.text('集成'));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const ValueKey('gateway-profile-chip-1')));
await tester.pumpAndSettle();
expect(find.text('配置码'), findsNothing);
expect(
find.byKey(const ValueKey('gateway-setup-code-field')),
findsNothing,
);
expect(find.byKey(const ValueKey('gateway-host-field')), findsOneWidget);
});
testWidgets('SettingsPage diagnostics tab filters and clears runtime logs', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
controller.runtime.addRuntimeLogForTest(
level: 'info',
category: 'connect',
message: 'connected remote gateway',
);
controller.runtime.addRuntimeLogForTest(
level: 'warn',
category: 'pairing',
message: 'pairing required',
);
await pumpPage(tester, child: SettingsPage(controller: controller));
await tester.tap(find.text('诊断'));
await tester.pumpAndSettle();
expect(find.byKey(const ValueKey('runtime-log-card')), findsOneWidget);
expect(find.textContaining('connected remote gateway'), findsOneWidget);
expect(find.textContaining('pairing required'), findsOneWidget);
await tester.enterText(
find.byKey(const ValueKey('runtime-log-filter')),
'pairing',
);
await tester.pump(const Duration(milliseconds: 200));
expect(find.textContaining('connected remote gateway'), findsNothing);
expect(find.textContaining('pairing required'), findsOneWidget);
await tester.tap(find.text('清空'));
await tester.pump(const Duration(milliseconds: 200));
expect(controller.runtimeLogs, isEmpty);
});
testWidgets('SettingsPage hides tabs disabled by feature manifest', (
WidgetTester tester,
) async {
final manifest = UiFeatureManifest.fallback()
.copyWithFeature(
platform: UiFeaturePlatform.desktop,
module: 'settings',
feature: 'diagnostics',
enabled: false,
)
.copyWithFeature(
platform: UiFeaturePlatform.desktop,
module: 'settings',
feature: 'experimental',
enabled: false,
);
final controller = await createTestController(
tester,
uiFeatureManifest: manifest,
);
await pumpPage(
tester,
child: SettingsPage(controller: controller),
platform: TargetPlatform.macOS,
);
expect(find.text('诊断'), findsNothing);
expect(find.text('实验特性'), findsNothing);
});
testWidgets(
'SettingsPage clears local assistant state with double confirmation',
(WidgetTester tester) async {
final controller = await createTestController(tester);
await pumpPage(tester, child: SettingsPage(controller: controller));
await tester.tap(find.text('诊断'));
await tester.pump(const Duration(milliseconds: 300));
expect(
find.byKey(const ValueKey('assistant-local-state-card')),
findsOneWidget,
);
await tester.tap(
find.byKey(const ValueKey('assistant-local-state-clear-button')),
);
await tester.pump(const Duration(milliseconds: 300));
final confirmButtonFinder = find.widgetWithText(FilledButton, '确认清理');
final confirmButtonBefore = tester.widget<FilledButton>(
confirmButtonFinder,
);
expect(confirmButtonBefore.onPressed, isNull);
await tester.tap(
find.byKey(const ValueKey('assistant-local-state-clear-confirm')),
);
await tester.pump(const Duration(milliseconds: 300));
final confirmButtonAfter = tester.widget<FilledButton>(
confirmButtonFinder,
);
expect(confirmButtonAfter.onPressed, isNotNull);
},
);
testWidgets('SettingsPage detail mode returns to overview', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
controller.openSettings(
detail: SettingsDetailPage.gatewayConnection,
navigationContext: SettingsNavigationContext(
rootLabel: '设置',
destination: WorkspaceDestination.settings,
sectionLabel: SettingsTab.gateway.label,
settingsTab: SettingsTab.gateway,
),
);
await pumpPage(
tester,
child: SettingsPage(
controller: controller,
initialTab: controller.settingsTab,
initialDetail: controller.settingsDetail,
navigationContext: controller.settingsNavigationContext,
),
);
expect(find.text('Gateway 连接参数'), findsWidgets);
expect(find.text('返回概览'), findsOneWidget);
await tester.tap(find.text('返回概览'));
await tester.pumpAndSettle();
expect(controller.settingsDetail, isNull);
expect(find.text('搜索设置'), findsOneWidget);
});
testWidgets('SettingsPage expands optional LLM endpoints with add button', (
WidgetTester tester,
) async {
final controller = await createTestController(tester);
controller.openSettings(
detail: SettingsDetailPage.aiGatewayIntegration,
navigationContext: SettingsNavigationContext(
rootLabel: '设置',
destination: WorkspaceDestination.settings,
sectionLabel: SettingsTab.gateway.label,
settingsTab: SettingsTab.gateway,
),
);
await pumpPage(
tester,
child: SettingsPage(
controller: controller,
initialTab: controller.settingsTab,
initialDetail: controller.settingsDetail,
navigationContext: controller.settingsNavigationContext,
),
);
expect(find.byKey(const ValueKey('llm-endpoint-chip-0')), findsOneWidget);
expect(find.byKey(const ValueKey('llm-endpoint-chip-1')), findsNothing);
await tester.tap(find.byKey(const ValueKey('llm-endpoint-add-button')));
await tester.pumpAndSettle();
expect(
find.descendant(
of: find.byKey(const ValueKey('llm-endpoint-chip-0')),
matching: find.textContaining('主 LLM API'),
),
findsOneWidget,
);
expect(
find.descendant(
of: find.byKey(const ValueKey('llm-endpoint-chip-1')),
matching: find.textContaining('Ollama 本地'),
),
findsOneWidget,
);
expect(find.text('连接源详情'), findsOneWidget);
expect(find.textContaining('自定义连接源'), findsNothing);
expect(find.byKey(const ValueKey('llm-endpoint-chip-1')), findsOneWidget);
expect(
find.byKey(const ValueKey('llm-endpoint-panel-ollamaLocal')),
findsOneWidget,
);
});
}