Skip to content

Commit dc64784

Browse files
committed
test: add test for all dashbot pages
1 parent 15821a0 commit dc64784

File tree

4 files changed

+808
-0
lines changed

4 files changed

+808
-0
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
import 'package:apidash/dashbot/core/constants/constants.dart';
2+
import 'package:apidash/dashbot/features/chat/models/chat_message.dart';
3+
import 'package:apidash/dashbot/features/chat/models/chat_state.dart';
4+
import 'package:apidash/dashbot/features/chat/view/pages/dashbot_chat_page.dart';
5+
import 'package:apidash/dashbot/features/chat/view/widgets/dashbot_task_buttons.dart';
6+
import 'package:apidash/dashbot/features/chat/viewmodel/chat_viewmodel.dart';
7+
import 'package:apidash/providers/collection_providers.dart';
8+
import 'package:flutter/material.dart';
9+
import 'package:flutter_markdown/flutter_markdown.dart';
10+
import 'package:flutter_riverpod/flutter_riverpod.dart';
11+
import 'package:flutter_test/flutter_test.dart';
12+
13+
import 'test_utils.dart';
14+
15+
void main() {
16+
Widget createChatScreen({
17+
List<Override> overrides = const [],
18+
ChatMessageType? initialTask,
19+
}) {
20+
return ProviderScope(
21+
overrides: [
22+
// Override the selectedRequestModelProvider to prevent Hive dependency issues
23+
selectedRequestModelProvider.overrideWith((ref) => null),
24+
...overrides,
25+
],
26+
child: MaterialApp(
27+
home: Scaffold(
28+
body: ChatScreen(initialTask: initialTask),
29+
),
30+
),
31+
);
32+
}
33+
34+
testWidgets('ChatScreen shows empty-state prompt when idle', (tester) async {
35+
late SpyChatViewmodel spy;
36+
await tester.pumpWidget(
37+
createChatScreen(
38+
overrides: [
39+
chatViewmodelProvider.overrideWith((ref) {
40+
spy = SpyChatViewmodel(ref);
41+
spy.setState(const ChatState());
42+
return spy;
43+
}),
44+
],
45+
),
46+
);
47+
await tester.pump();
48+
49+
expect(find.text('Ask me anything!'), findsOneWidget);
50+
expect(spy.sendMessageCalls, isEmpty);
51+
});
52+
53+
testWidgets('ChatScreen triggers initial task without user input',
54+
(tester) async {
55+
late SpyChatViewmodel spy;
56+
await tester.pumpWidget(
57+
createChatScreen(
58+
initialTask: ChatMessageType.generateDoc,
59+
overrides: [
60+
chatViewmodelProvider.overrideWith((ref) {
61+
spy = SpyChatViewmodel(ref);
62+
spy.setState(const ChatState());
63+
return spy;
64+
}),
65+
],
66+
),
67+
);
68+
69+
await tester.pump();
70+
71+
expect(spy.sendMessageCalls.length, 1);
72+
expect(spy.sendMessageCalls.first.text, isEmpty);
73+
expect(spy.sendMessageCalls.first.type, ChatMessageType.generateDoc);
74+
expect(spy.sendMessageCalls.first.countAsUser, isFalse);
75+
});
76+
77+
testWidgets('ChatScreen toggles task suggestions panel', (tester) async {
78+
late SpyChatViewmodel spy;
79+
await tester.pumpWidget(
80+
createChatScreen(
81+
overrides: [
82+
chatViewmodelProvider.overrideWith((ref) {
83+
spy = SpyChatViewmodel(ref);
84+
spy.setState(const ChatState());
85+
return spy;
86+
}),
87+
],
88+
),
89+
);
90+
91+
expect(find.byType(DashbotTaskButtons), findsNothing);
92+
93+
await tester.tap(find.byIcon(Icons.help_outline_rounded));
94+
await tester.pump();
95+
96+
expect(find.byType(DashbotTaskButtons), findsOneWidget);
97+
});
98+
99+
testWidgets('Clear chat icon delegates to viewmodel', (tester) async {
100+
late SpyChatViewmodel spy;
101+
await tester.pumpWidget(
102+
createChatScreen(
103+
overrides: [
104+
chatViewmodelProvider.overrideWith((ref) {
105+
spy = SpyChatViewmodel(ref);
106+
spy.setState(const ChatState());
107+
return spy;
108+
}),
109+
],
110+
),
111+
);
112+
113+
await tester.tap(find.byIcon(Icons.clear_all_rounded));
114+
await tester.pump();
115+
116+
expect(spy.clearCalled, isTrue);
117+
});
118+
119+
testWidgets('Submitting text sends general chat message', (tester) async {
120+
late SpyChatViewmodel spy;
121+
await tester.pumpWidget(
122+
createChatScreen(
123+
overrides: [
124+
chatViewmodelProvider.overrideWith((ref) {
125+
spy = SpyChatViewmodel(ref);
126+
spy.setState(const ChatState());
127+
return spy;
128+
}),
129+
],
130+
),
131+
);
132+
133+
await tester.enterText(find.byType(TextField), 'Hello Dashbot');
134+
await tester.tap(find.byIcon(Icons.send_rounded));
135+
await tester.pump();
136+
137+
expect(spy.sendMessageCalls.length, 1);
138+
expect(spy.sendMessageCalls.first.text, 'Hello Dashbot');
139+
expect(spy.sendMessageCalls.first.type, ChatMessageType.general);
140+
});
141+
142+
testWidgets('Streaming state renders temporary ChatBubble', (tester) async {
143+
late SpyChatViewmodel spy;
144+
await tester.pumpWidget(
145+
createChatScreen(
146+
overrides: [
147+
chatViewmodelProvider.overrideWith((ref) {
148+
spy = SpyChatViewmodel(ref);
149+
spy.setState(const ChatState(
150+
isGenerating: true, currentStreamingResponse: 'Streaming...'));
151+
return spy;
152+
}),
153+
],
154+
),
155+
);
156+
157+
await tester.pump();
158+
159+
final markdown =
160+
tester.widget<MarkdownBody>(find.byType(MarkdownBody).first);
161+
expect(markdown.data, 'Streaming...');
162+
});
163+
164+
testWidgets('Existing chat messages render in list', (tester) async {
165+
late SpyChatViewmodel spy;
166+
final messages = [
167+
ChatMessage(
168+
id: '1',
169+
content: 'First',
170+
role: MessageRole.user,
171+
timestamp: DateTime(2024),
172+
),
173+
ChatMessage(
174+
id: '2',
175+
content: 'Second',
176+
role: MessageRole.system,
177+
timestamp: DateTime(2024, 2),
178+
),
179+
];
180+
181+
await tester.pumpWidget(
182+
createChatScreen(
183+
overrides: [
184+
chatViewmodelProvider.overrideWith((ref) {
185+
spy = SpyChatViewmodel(ref);
186+
spy.setMessages(messages);
187+
spy.setState(ChatState(chatSessions: {'global': messages}));
188+
return spy;
189+
}),
190+
],
191+
),
192+
);
193+
194+
await tester.pump();
195+
196+
expect(find.byType(ListView), findsOneWidget);
197+
expect(find.text('First'), findsOneWidget);
198+
expect(find.text('Second'), findsOneWidget);
199+
});
200+
201+
testWidgets('TextField onSubmitted sends message', (tester) async {
202+
late SpyChatViewmodel spy;
203+
await tester.pumpWidget(
204+
createChatScreen(
205+
overrides: [
206+
chatViewmodelProvider.overrideWith((ref) {
207+
spy = SpyChatViewmodel(ref);
208+
spy.setState(const ChatState());
209+
return spy;
210+
}),
211+
],
212+
),
213+
);
214+
215+
await tester.enterText(find.byType(TextField), 'Test message');
216+
await tester.testTextInput.receiveAction(TextInputAction.done);
217+
await tester.pump();
218+
219+
expect(spy.sendMessageCalls.length, 1);
220+
expect(spy.sendMessageCalls.first.text, 'Test message');
221+
expect(spy.sendMessageCalls.first.type, ChatMessageType.general);
222+
});
223+
224+
testWidgets('Task suggestions panel hides when generating starts',
225+
(tester) async {
226+
late SpyChatViewmodel spy;
227+
await tester.pumpWidget(
228+
createChatScreen(
229+
overrides: [
230+
chatViewmodelProvider.overrideWith((ref) {
231+
spy = SpyChatViewmodel(ref);
232+
spy.setState(const ChatState());
233+
return spy;
234+
}),
235+
],
236+
),
237+
);
238+
239+
// First show task suggestions
240+
await tester.tap(find.byIcon(Icons.help_outline_rounded));
241+
await tester.pump();
242+
expect(find.byType(DashbotTaskButtons), findsOneWidget);
243+
244+
// Then start generating - this should hide the task suggestions
245+
spy.setState(const ChatState(isGenerating: true));
246+
await tester.pump();
247+
248+
expect(find.byType(DashbotTaskButtons), findsNothing);
249+
});
250+
251+
testWidgets('Scroll animation triggers on streaming response changes',
252+
(tester) async {
253+
late SpyChatViewmodel spy;
254+
await tester.pumpWidget(
255+
createChatScreen(
256+
overrides: [
257+
chatViewmodelProvider.overrideWith((ref) {
258+
spy = SpyChatViewmodel(ref);
259+
spy.setState(const ChatState(
260+
isGenerating: true,
261+
currentStreamingResponse: 'Initial...',
262+
));
263+
return spy;
264+
}),
265+
],
266+
),
267+
);
268+
269+
await tester.pump();
270+
271+
// Change the streaming response - this should trigger scroll
272+
spy.setState(const ChatState(
273+
isGenerating: true,
274+
currentStreamingResponse: 'Updated streaming response...',
275+
));
276+
await tester.pump();
277+
278+
// Verify scrolling behavior by checking that the new content is rendered
279+
expect(find.text('Updated streaming response...'), findsOneWidget);
280+
});
281+
282+
testWidgets('Scroll animation triggers when generation completes',
283+
(tester) async {
284+
late SpyChatViewmodel spy;
285+
final messages = [
286+
ChatMessage(
287+
id: '1',
288+
content: 'Generated response',
289+
role: MessageRole.system,
290+
timestamp: DateTime(2024),
291+
),
292+
];
293+
294+
await tester.pumpWidget(
295+
createChatScreen(
296+
overrides: [
297+
chatViewmodelProvider.overrideWith((ref) {
298+
spy = SpyChatViewmodel(ref);
299+
spy.setState(const ChatState(isGenerating: true));
300+
return spy;
301+
}),
302+
],
303+
),
304+
);
305+
306+
await tester.pump();
307+
308+
// Complete generation - this should trigger scroll
309+
spy.setMessages(messages);
310+
spy.setState(ChatState(
311+
isGenerating: false,
312+
chatSessions: {'global': messages},
313+
));
314+
await tester.pump();
315+
316+
expect(find.text('Generated response'), findsOneWidget);
317+
});
318+
}

0 commit comments

Comments
 (0)