screen-locker/stronglift_replacement/workout_app/test/widget_test.dart
Krzysztof kuhy Rudnicki b24718710e Make workout app home screen text-free
Home card: plan letter + state glyph, play-glyph buttons. Sync card
and banner: state glyph, age as 5m/3h/2d, action glyph. Old words
stay as spoken labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 01:28:47 +01:00

85 lines
3.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:workout_app/main.dart';
import 'package:workout_app/screens/home_screen.dart';
import 'package:workout_app/sandbox/sandbox.dart';
import 'package:workout_app/services/http_server_service.dart';
import 'package:workout_app/services/storage_service.dart';
import 'package:workout_app/ui/theme.dart';
import 'fake_secure_storage.dart';
void main() {
setUpAll(() {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
});
setUp(() async {
// These tests build the whole WorkoutApp, so they cannot inject a sync
// probe the way home_screen_test does. The home screen now syncs in the
// background on open, which reaches FlutterSecureStorage -- whose real
// platform channel throws under `flutter test`. Install the fake instead
// of letting an unrelated MissingPluginException fail these.
installFakeSecureStorage();
StorageService.resetForTesting();
await StorageService.init();
});
testWidgets('WorkoutApp renders HomeScreen', (tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(const WorkoutApp());
await Future<void>.delayed(const Duration(milliseconds: 300));
});
await tester.pump();
expect(find.byType(HomeScreen), findsOneWidget);
});
testWidgets('the sandbox flavor wears its ribbon on every screen', (
tester,
) async {
Sandbox.enabled = true;
addTearDown(() => Sandbox.enabled = false);
await tester.runAsync(() async {
await tester.pumpWidget(const WorkoutApp());
await Future<void>.delayed(const Duration(milliseconds: 300));
});
await tester.pump();
expect(find.byType(Banner), findsOneWidget);
// A bare red ribbon: the colour and the corner are the cue, no word.
final banner = tester.widget<Banner>(find.byType(Banner));
expect(banner.message, isEmpty);
expect(banner.color, buildAppTheme().colorScheme.error);
});
testWidgets('the daily build has no ribbon', (tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(const WorkoutApp());
await Future<void>.delayed(const Duration(milliseconds: 300));
});
await tester.pump();
expect(find.byType(Banner), findsNothing);
});
testWidgets('app-lifecycle changes drive the HTTP server', (tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(const WorkoutApp());
await Future<void>.delayed(const Duration(milliseconds: 100));
// A valid forward transition path that visits every handled state.
for (final state in const [
AppLifecycleState.resumed,
AppLifecycleState.inactive,
AppLifecycleState.hidden,
AppLifecycleState.paused,
AppLifecycleState.detached,
]) {
tester.binding.handleAppLifecycleStateChanged(state);
await Future<void>.delayed(const Duration(milliseconds: 5));
}
await HttpServerService.instance.stop();
});
await tester.pump();
});
}