summaryrefslogtreecommitdiff
path: root/player-android/test/screens
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-22 09:34:24 +0300
committerPaul Buetow <paul@buetow.org>2026-05-22 09:34:24 +0300
commitbcf89f586c6dd80f240ac3fb80d01ee3c5f6bcd0 (patch)
tree594f10487ad313790798531863788b001dd662f2 /player-android/test/screens
parentcce598d8b698ecf4490127d5fad9bd0995ec1050 (diff)
Fix Material 3 theming review issues (task fb)
- Roll back ThemeNotifier state on SharedPreferences write failure so in-memory and disk never diverge - Fix stale comment: scaffoldBackgroundColor ← --bg-body (not background) - Fix source attribution: theme.css not docs/theming.md - Make _ThemeToggle const-constructible; remove incorrect ignore comment - Inline _buildSegmentedButton into _ThemeToggle.build (was a trivial passthrough) - Add defensive isNotEmpty comment on SegmentedButton.onSelectionChanged - Hoist buildLightTheme()/buildDarkTheme() to module-level finals in main.dart so ThemeData is built once at startup rather than on every rebuild - Add _ThemeToggle tests: initial segment selection and segment tap dispatch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'player-android/test/screens')
-rw-r--r--player-android/test/screens/settings_screen_test.dart110
1 files changed, 110 insertions, 0 deletions
diff --git a/player-android/test/screens/settings_screen_test.dart b/player-android/test/screens/settings_screen_test.dart
index 71c2d7d..ecbc94d 100644
--- a/player-android/test/screens/settings_screen_test.dart
+++ b/player-android/test/screens/settings_screen_test.dart
@@ -21,6 +21,7 @@ import 'package:player_android/api/dio_client.dart';
import 'package:player_android/providers/api_client_provider.dart';
import 'package:player_android/providers/auth_state_provider.dart';
import 'package:player_android/providers/settings_provider.dart';
+import 'package:player_android/providers/theme_provider.dart';
import 'package:player_android/screens/settings_screen.dart';
// ---------------------------------------------------------------------------
@@ -65,6 +66,25 @@ class _FakeSettingsNotifier extends SettingsNotifier {
}
}
+/// In-memory [ThemeNotifier] that bypasses [SharedPreferences] in tests.
+///
+/// Starts at [ThemeMode.system] (the default) and records the last mode passed
+/// to [setThemeMode] so tests can assert on the captured value.
+class _FakeThemeNotifier extends ThemeNotifier {
+ // Tracks the last mode applied via setThemeMode for test assertions.
+ ThemeMode? capturedMode;
+
+ @override
+ Future<ThemeMode> build() async => ThemeMode.system;
+
+ @override
+ Future<void> setThemeMode(ThemeMode mode) async {
+ capturedMode = mode;
+ // Mirror the production behaviour: update in-memory state immediately.
+ state = AsyncData(mode);
+ }
+}
+
// ---------------------------------------------------------------------------
// Helper: pump SettingsScreen inside a minimal ProviderScope.
// ---------------------------------------------------------------------------
@@ -260,6 +280,96 @@ void main() {
});
// --------------------------------------------------------------------------
+ // Theme toggle
+ // --------------------------------------------------------------------------
+
+ group('theme toggle', () {
+ testWidgets('renders with system segment selected by default',
+ (tester) async {
+ final fakeStorage = _FakeTokenStorage().._token = 'alice';
+ final fakeSettings = _FakeSettingsNotifier('http://10.0.2.2:8080');
+ final fakeTheme = _FakeThemeNotifier();
+
+ final router = GoRouter(
+ initialLocation: '/',
+ routes: [
+ GoRoute(
+ path: '/',
+ builder: (_, __) => const SettingsScreen(),
+ ),
+ GoRoute(
+ path: '/login',
+ builder: (_, __) => const Scaffold(body: Text('Login')),
+ ),
+ ],
+ );
+
+ await tester.pumpWidget(
+ ProviderScope(
+ overrides: [
+ tokenStorageProvider.overrideWithValue(fakeStorage),
+ settingsProvider.overrideWith(() => fakeSettings),
+ themeProvider.overrideWith(() => fakeTheme),
+ ],
+ child: MaterialApp.router(routerConfig: router),
+ ),
+ );
+ await tester.pumpAndSettle();
+
+ // The segmented button should be present with the system segment selected.
+ final button = tester.widget<SegmentedButton<ThemeMode>>(
+ find.byKey(const Key('settings_theme_toggle')),
+ );
+ expect(button.selected, equals({ThemeMode.system}));
+ });
+
+ testWidgets('tapping a segment calls setThemeMode with the right ThemeMode',
+ (tester) async {
+ final fakeStorage = _FakeTokenStorage().._token = 'alice';
+ final fakeSettings = _FakeSettingsNotifier('http://10.0.2.2:8080');
+ final fakeTheme = _FakeThemeNotifier();
+
+ final router = GoRouter(
+ initialLocation: '/',
+ routes: [
+ GoRoute(
+ path: '/',
+ builder: (_, __) => const SettingsScreen(),
+ ),
+ GoRoute(
+ path: '/login',
+ builder: (_, __) => const Scaffold(body: Text('Login')),
+ ),
+ ],
+ );
+
+ await tester.pumpWidget(
+ ProviderScope(
+ overrides: [
+ tokenStorageProvider.overrideWithValue(fakeStorage),
+ settingsProvider.overrideWith(() => fakeSettings),
+ themeProvider.overrideWith(() => fakeTheme),
+ ],
+ child: MaterialApp.router(routerConfig: router),
+ ),
+ );
+ await tester.pumpAndSettle();
+
+ // Scroll the segmented button into view before tapping — the settings
+ // screen content may exceed the test viewport height.
+ await tester.ensureVisible(find.byKey(const Key('settings_theme_toggle')));
+ await tester.pumpAndSettle();
+
+ // Tap the "Dark" segment label.
+ await tester.tap(find.text('Dark'));
+ await tester.pumpAndSettle();
+
+ // The fake notifier should have received ThemeMode.dark.
+ expect(fakeTheme.capturedMode, equals(ThemeMode.dark));
+ });
+ });
+
+ // --------------------------------------------------------------------------
// Logout flow
// --------------------------------------------------------------------------