mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-14 22:50:49 +01:00
Cover history_screen.dart to 100%
Add an exercise drill-down test (dropdown select -> streak stats card, weight chart, exercise calendar nav, session tiles with warmup/reps variants) and a same-date test for the chart painter's tRange==0 branch. coverage:ignore the _load exercise-state branch (unreachable — selection goes through _pickExercise, not _load). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWkAKQ48Jhg5iSPkzh8NsH
This commit is contained in:
parent
e14cdf9997
commit
a27efefefc
@ -51,7 +51,12 @@ class _HistoryScreenState extends State<HistoryScreen> {
|
||||
}
|
||||
ExerciseState? state;
|
||||
if (_selected != _kTotal) {
|
||||
// Unreachable in practice: _load only runs from initState, where
|
||||
// _selected is still _kTotal; exercise selection goes through
|
||||
// _pickExercise, which loads its own state.
|
||||
// coverage:ignore-start
|
||||
state = await StorageService.instance.getExerciseState(_selected);
|
||||
// coverage:ignore-end
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
|
||||
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
import 'package:workout_app/models/workout_plan.dart';
|
||||
import 'package:workout_app/screens/history_screen.dart';
|
||||
import 'package:workout_app/services/storage_service.dart';
|
||||
|
||||
@ -153,4 +154,126 @@ void main() {
|
||||
await _pump(tester, _wrap());
|
||||
expect(find.byType(HistoryScreen), findsOneWidget);
|
||||
});
|
||||
|
||||
// JSON for one session containing [name] at [weight].
|
||||
String _exerciseSessionJson(
|
||||
String name,
|
||||
double weight, {
|
||||
required bool ok,
|
||||
required bool warmup,
|
||||
required bool withSets,
|
||||
}) {
|
||||
return jsonEncode({
|
||||
'exercises': [
|
||||
{
|
||||
'name': name,
|
||||
'targetSets': 3,
|
||||
'targetReps': 5,
|
||||
'targetWeight': weight,
|
||||
'warmupDone': warmup,
|
||||
'succeeded': ok,
|
||||
'sets': withSets
|
||||
? [
|
||||
{
|
||||
'targetReps': 5,
|
||||
'doneReps': 5,
|
||||
'weight': weight,
|
||||
'succeeded': true,
|
||||
},
|
||||
{
|
||||
'targetReps': 5,
|
||||
'doneReps': 4,
|
||||
'weight': weight,
|
||||
'succeeded': false,
|
||||
},
|
||||
]
|
||||
: <Map<String, dynamic>>[],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
testWidgets('selecting an exercise shows the drill-down view', (tester) async {
|
||||
// Use a real progression-tracked exercise so getExerciseState is non-null
|
||||
// and the streak stats card renders. Three distinct weights on three dates
|
||||
// give the weight chart >= 2 points with a real range (painter fully runs).
|
||||
final name = workoutA.first.name;
|
||||
await _seed(
|
||||
tester,
|
||||
_exerciseSessionJson(name, 20, ok: true, warmup: true, withSets: true),
|
||||
date: '2024-06-01',
|
||||
);
|
||||
await _seed(
|
||||
tester,
|
||||
_exerciseSessionJson(name, 22.5, ok: false, warmup: false, withSets: false),
|
||||
date: '2024-06-08',
|
||||
succeeded: false,
|
||||
);
|
||||
await _seed(
|
||||
tester,
|
||||
_exerciseSessionJson(name, 25, ok: true, warmup: true, withSets: true),
|
||||
date: '2024-06-15',
|
||||
);
|
||||
|
||||
await _pump(tester, _wrap());
|
||||
|
||||
// Open the dropdown and pick the exercise; _pickExercise awaits a DB read,
|
||||
// and the menu open/close animation runs on real timers under runAsync, so
|
||||
// drive the whole interaction on the real loop.
|
||||
await tester.runAsync(() async {
|
||||
await tester.tap(find.byType(DropdownButton<String>));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 400));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text(name).last);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 400));
|
||||
await tester.pump();
|
||||
});
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
// Exercise view rendered: chart + streak stats card near the top.
|
||||
expect(find.text('WEIGHT OVER TIME'), findsOneWidget);
|
||||
expect(find.textContaining('more to'), findsWidgets);
|
||||
|
||||
// Exercise-view calendar month nav (covers its onPrev/onNext closures).
|
||||
await tester.tap(find.byIcon(Icons.chevron_right).first);
|
||||
await tester.pump();
|
||||
await tester.tap(find.byIcon(Icons.chevron_left).first);
|
||||
await tester.pump();
|
||||
|
||||
// The session list + name label sit below the fold; scroll to build them.
|
||||
await tester.scrollUntilVisible(
|
||||
find.text(name.toUpperCase()),
|
||||
300,
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
expect(find.text(name.toUpperCase()), findsOneWidget);
|
||||
// Session tiles: warmup marker and reps summary both present.
|
||||
expect(find.textContaining('warmup'), findsWidgets);
|
||||
expect(find.textContaining('reps:'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('chart handles two points on the same date (tRange == 0)',
|
||||
(tester) async {
|
||||
// Two sessions on the SAME date give the total chart two points that share
|
||||
// a timestamp, exercising the painter's tRange == 0 (centre-x) branch.
|
||||
String vol(double w) => jsonEncode({
|
||||
'exercises': [
|
||||
{
|
||||
'name': 'Squat',
|
||||
'targetSets': 3,
|
||||
'targetReps': 5,
|
||||
'targetWeight': w,
|
||||
'warmupDone': false,
|
||||
'succeeded': true,
|
||||
'sets': <Map<String, dynamic>>[],
|
||||
},
|
||||
],
|
||||
});
|
||||
await _seed(tester, vol(20), date: '2024-06-01');
|
||||
await _seed(tester, vol(40), date: '2024-06-01');
|
||||
await _pump(tester, _wrap());
|
||||
expect(find.byType(HistoryScreen), findsOneWidget);
|
||||
expect(find.text('WEIGHT OVER TIME').evaluate(), isEmpty); // total view
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user