mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-14 17:50:56 +01:00
Cover home_screen.dart to 100%
Cover the post-navigation reload paths (return from workout via Resume, return from settings), the resume-button closure, and the empty-address placeholder. Expose ServerAddressTile so its no-address fallback can be unit-tested directly (the test host always has network interfaces). 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
b76d156ae3
commit
5fe4222dfd
@ -153,7 +153,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
onResume: () => _openWorkout(resume: true),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_ServerAddressTile(addresses: _serverAddresses),
|
||||
ServerAddressTile(addresses: _serverAddresses),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -278,8 +278,12 @@ class _WorkoutCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ServerAddressTile extends StatelessWidget {
|
||||
const _ServerAddressTile({required this.addresses});
|
||||
/// Displays the LAN addresses the workout HTTP server is reachable at, or a
|
||||
/// "Server not started" placeholder when none are available. Public so the
|
||||
/// empty-address fallback can be exercised in a widget test.
|
||||
class ServerAddressTile extends StatelessWidget {
|
||||
/// Creates a tile listing [addresses] (may be empty).
|
||||
const ServerAddressTile({required this.addresses, super.key});
|
||||
|
||||
final List<String> addresses;
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
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/home_screen.dart';
|
||||
import 'package:workout_app/screens/workout_screen.dart';
|
||||
import 'package:workout_app/services/storage_service.dart';
|
||||
|
||||
import '../fake_secure_storage.dart';
|
||||
@ -118,4 +120,78 @@ void main() {
|
||||
// The post-frame auto-resume pushed the workout screen.
|
||||
expect(find.textContaining('Workout A'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('active session shows Resume, returns (98) and re-enters (153)',
|
||||
(tester) async {
|
||||
// A fully-formed active session so WorkoutScreen restores cleanly (its
|
||||
// _restoreFromSaved expects startTimeMs + per-exercise tapped/doneReps).
|
||||
await tester.runAsync(
|
||||
() => StorageService.instance.saveActiveSession({
|
||||
'workoutType': 'A',
|
||||
'startTimeMs': DateTime.now().millisecondsSinceEpoch,
|
||||
'tapped': [for (final e in workoutA) List<bool>.filled(e.sets, false)],
|
||||
'doneReps': [for (final e in workoutA) List<int>.filled(e.sets, e.reps)],
|
||||
'warmupTapped': List<bool>.filled(workoutA.length, false),
|
||||
}),
|
||||
);
|
||||
// Drive the whole first-load + post-frame auto-resume push on the real loop
|
||||
// (auto-resume awaits getCurrentExercises, which hangs under FakeAsync).
|
||||
await tester.runAsync(() async {
|
||||
await tester.pumpWidget(_wrap());
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
await tester.pump(); // fire the post-frame auto-resume callback
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
});
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(find.byType(WorkoutScreen), findsOneWidget);
|
||||
|
||||
// Return to home; auto-resume is now consumed so the Resume button shows.
|
||||
await tester.runAsync(() async {
|
||||
tester.state<NavigatorState>(find.byType(Navigator).last).pop();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
});
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(find.byType(WorkoutScreen), findsNothing);
|
||||
expect(find.text('Resume Workout'), findsOneWidget);
|
||||
|
||||
// Tapping Resume invokes onResume -> _openWorkout(resume:true) (line 153).
|
||||
await tester.runAsync(() async {
|
||||
await tester.tap(find.text('Resume Workout'));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
});
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(find.byType(WorkoutScreen), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('returning from settings reloads the home screen',
|
||||
(tester) async {
|
||||
installFakeSecureStorage(); // SettingsScreen reads the sync token on init
|
||||
await _pump(tester, _wrap());
|
||||
await tester.tap(find.byIcon(Icons.settings));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.text('Settings'), findsOneWidget);
|
||||
|
||||
// Pop back; the trailing _load() (line 135) needs the real loop for DB I/O.
|
||||
await tester.runAsync(() async {
|
||||
tester.state<NavigatorState>(find.byType(Navigator).last).pop();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
});
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.text('Workout Tracker'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('ServerAddressTile shows a placeholder when no addresses',
|
||||
(tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Scaffold(body: ServerAddressTile(addresses: <String>[])),
|
||||
),
|
||||
);
|
||||
expect(find.text('Server not started'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user