93 lines
3.4 KiB
Markdown
93 lines
3.4 KiB
Markdown
# TT-013 — Widget Tests
|
|
|
|
**Type:** Task
|
|
**Priority:** Medium
|
|
**Labels:** testing, widget-tests
|
|
**Depends on:** TT-006, TT-007, TT-008, TT-009, TT-010, TT-011
|
|
**Blocks:** —
|
|
|
|
---
|
|
|
|
## Summary
|
|
Write widget tests for each feature screen. Tests verify correct rendering
|
|
of UI states, user interactions, and provider integration. All provider
|
|
dependencies are overridden with mocks via `mocktail`.
|
|
|
|
## Scope
|
|
|
|
| Test file | Screen | Key scenarios |
|
|
|-----------|--------|---------------|
|
|
| `test/features/timer/timer_screen_test.dart` | TimerScreen | idle state, running state, start tap, stop tap |
|
|
| `test/features/entries/entries_screen_test.dart` | EntriesScreen | empty state, list renders, swipe delete, add form |
|
|
| `test/features/projects/projects_screen_test.dart` | ProjectsScreen | empty state, tile renders, create form, archive |
|
|
| `test/features/reports/reports_screen_test.dart` | ReportsScreen | week tab, period navigation, chart renders |
|
|
| `test/features/settings/settings_screen_test.dart` | SettingsScreen | theme picker, language picker, export sheet opens |
|
|
|
|
## Widget Test Pattern
|
|
```dart
|
|
Widget buildTestApp({List<Override> overrides = const []}) {
|
|
return ProviderScope(
|
|
overrides: overrides,
|
|
child: MaterialApp(
|
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
|
supportedLocales: AppLocalizations.supportedLocales,
|
|
home: const TimerScreen(),
|
|
),
|
|
);
|
|
}
|
|
|
|
testWidgets('shows START button when idle', (tester) async {
|
|
when(() => mockTimerNotifier.build()).thenReturn(const TimerState.idle());
|
|
|
|
await tester.pumpWidget(buildTestApp(overrides: [
|
|
timerNotifierProvider.overrideWith(() => mockTimerNotifier),
|
|
]));
|
|
|
|
expect(find.text('Start'), findsOneWidget);
|
|
expect(find.text('Stop'), findsNothing);
|
|
});
|
|
```
|
|
|
|
## Key Widget Test Cases per Screen
|
|
|
|
### TimerScreen
|
|
- [ ] Idle: START visible, STOP hidden, elapsed shows `00:00:00`
|
|
- [ ] Running: STOP visible, DISCARD visible, project name shown
|
|
- [ ] Tap START without project → button remains disabled
|
|
- [ ] Tap STOP → `timerNotifier.stop()` called once
|
|
- [ ] Tap DISCARD → confirmation dialog appears
|
|
|
|
### EntriesScreen
|
|
- [ ] Empty: placeholder text visible
|
|
- [ ] With entries: `EntryListTile` rendered for each
|
|
- [ ] Swipe left on entry → delete action visible
|
|
- [ ] Tap "+" → `EntryFormSheet` appears
|
|
- [ ] Date filter chip "Today" → filters visible entries
|
|
|
|
### ProjectsScreen
|
|
- [ ] Empty Active tab: "No projects yet" visible
|
|
- [ ] Tile shows project name and color dot
|
|
- [ ] Tap tile → edit form opens pre-filled
|
|
- [ ] Archive action → tile moves to Archived tab
|
|
|
|
### ReportsScreen
|
|
- [ ] Week tab shows 7 bar groups
|
|
- [ ] Tap ▶ → period label advances by one week
|
|
- [ ] Total duration card shows formatted duration
|
|
|
|
### SettingsScreen
|
|
- [ ] Theme dropdown shows current selection
|
|
- [ ] Changing theme → `themeModeProvider` updated
|
|
- [ ] Tap "Export data" → `ExportSheet` bottom sheet appears
|
|
|
|
## Acceptance Criteria
|
|
- [ ] All listed widget test files exist and pass with `flutter test`
|
|
- [ ] Each screen has ≥ 3 test cases
|
|
- [ ] No test relies on real DB or real providers
|
|
- [ ] Tests run in < 30 seconds total (no `sleep` / real async delays)
|
|
- [ ] `pumpAndSettle` used after async interactions
|
|
|
|
## Notes
|
|
- Use `find.byType()`, `find.text()`, `find.byKey()` — avoid pixel-exact finders
|
|
- Add semantic labels / keys to interactive widgets to simplify test selectors
|
|
- Golden tests (screenshot comparison) are optional for v1
|