main
dart 158 lines 4.87 KB
Raw
1 import 'package:flutter/material.dart';
2 import 'package:flutter_localizations/flutter_localizations.dart';
3 import 'package:flutter_riverpod/flutter_riverpod.dart';
4 import 'package:flutter_test/flutter_test.dart';
5 import 'package:localmind/core/providers/app_providers.dart';
6 import 'package:localmind/core/theme/app_theme.dart';
7 import 'package:localmind/features/chat/data/tools/tool_definition.dart';
8 import 'package:localmind/features/chat/providers/tooling_providers.dart';
9 import 'package:localmind/features/mcp/views/mcp_tools_screen.dart';
10 import 'package:localmind/features/settings/data/models/app_settings.dart';
11 import 'package:localmind/l10n/app_localizations.dart';
12 import 'package:shadcn_ui/shadcn_ui.dart';
13 import 'package:shared_preferences/shared_preferences.dart';
14
15 Widget _wrapWithApp({required Widget child, List<dynamic>? overrides}) {
16 return ProviderScope(
17 overrides: [...?overrides?.cast()],
18 child: ShadTheme(
19 data: AppTheme.lightShadTheme,
20 child: MaterialApp(
21 locale: const Locale('en'),
22 localizationsDelegates: const [
23 AppLocalizations.delegate,
24 GlobalMaterialLocalizations.delegate,
25 GlobalWidgetsLocalizations.delegate,
26 GlobalCupertinoLocalizations.delegate,
27 ],
28 supportedLocales: AppLocalizations.supportedLocales,
29 home: Scaffold(body: child),
30 ),
31 ),
32 );
33 }
34
35 void main() {
36 TestWidgetsFlutterBinding.ensureInitialized();
37
38 setUp(() {
39 SharedPreferences.setMockInitialValues({});
40 });
41
42 group('McpToolsScreen Widget Tests', () {
43 testWidgets('renders without throwing setState during build exception', (
44 tester,
45 ) async {
46 final List<ToolDefinition> tools = [
47 const ToolDefinition(
48 name: 'get_current_weather',
49 description: 'Get weather for location',
50 inputSchema: {},
51 providerType: ToolProviderType.mcp,
52 providerRef: 'weather_server',
53 ),
54 ];
55
56 await tester.pumpWidget(
57 _wrapWithApp(
58 overrides: [
59 availableToolsProvider.overrideWith((ref) => Future.value(tools)),
60 settingsProvider.overrideWith(
61 () => _TestSettingsNotifier(
62 AppSettings(mcpEnabled: true, newChatMcpEnabled: true),
63 ),
64 ),
65 ],
66 child: const McpToolsScreen(),
67 ),
68 );
69
70 // Verify no exceptions were thrown during first build/mount
71 expect(tester.takeException(), isNull);
72
73 await tester.pumpAndSettle();
74
75 expect(find.text('get_current_weather'), findsOneWidget);
76 expect(find.text('Get weather for location'), findsOneWidget);
77 expect(find.text('weather_server'), findsOneWidget);
78 expect(find.text('MCP'), findsOneWidget);
79 });
80
81 testWidgets('displays disabled warning when mcpEnabled is false', (
82 tester,
83 ) async {
84 await tester.pumpWidget(
85 _wrapWithApp(
86 overrides: [
87 settingsProvider.overrideWith(
88 () => _TestSettingsNotifier(AppSettings(mcpEnabled: false)),
89 ),
90 ],
91 child: const McpToolsScreen(),
92 ),
93 );
94
95 await tester.pumpAndSettle();
96
97 final l10n = lookupAppLocalizations(const Locale('en'));
98 expect(find.text(l10n.mcp_disabled_warning), findsOneWidget);
99 });
100
101 testWidgets('displays empty state when tools list is empty', (
102 tester,
103 ) async {
104 await tester.pumpWidget(
105 _wrapWithApp(
106 overrides: [
107 availableToolsProvider.overrideWith(
108 (ref) => Future.value(const <ToolDefinition>[]),
109 ),
110 settingsProvider.overrideWith(
111 () => _TestSettingsNotifier(
112 AppSettings(mcpEnabled: true, newChatMcpEnabled: true),
113 ),
114 ),
115 ],
116 child: const McpToolsScreen(),
117 ),
118 );
119
120 await tester.pumpAndSettle();
121
122 final l10n = lookupAppLocalizations(const Locale('en'));
123 expect(find.text(l10n.no_tools_registered), findsOneWidget);
124 });
125
126 testWidgets('displays error panel when availableToolsProvider fails', (
127 tester,
128 ) async {
129 await tester.pumpWidget(
130 _wrapWithApp(
131 overrides: [
132 availableToolsProvider.overrideWith(
133 (ref) => Future.error('Connection failed to MCP socket'),
134 ),
135 settingsProvider.overrideWith(
136 () => _TestSettingsNotifier(
137 AppSettings(mcpEnabled: true, newChatMcpEnabled: true),
138 ),
139 ),
140 ],
141 child: const McpToolsScreen(),
142 ),
143 );
144
145 await tester.pumpAndSettle();
146
147 expect(find.text('Connection failed to MCP socket'), findsOneWidget);
148 });
149 });
150 }
151
152 class _TestSettingsNotifier extends SettingsNotifier {
153 _TestSettingsNotifier(this._initial);
154 final AppSettings _initial;
155
156 @override
157 AppSettings build() => _initial;
158 }