utils/sync_settings_ui/test/sync_settings_screen_google_test.dart
Krzysztof kuhy Rudnicki db3d8f827f Let a host explain an absent Google button instead of hiding it
lyricanki's Linux desktop cannot use google_sign_in (no Linux
implementation) and its loopback replacement is dark until a Desktop OAuth
client exists. Hiding the button there makes the screen silently offer less
on one platform, which reads as a bug.

googleUnavailableReason renders a *disabled* button plus the reason. That is
not the "control that can never succeed" trap: the trap is a live-looking
button that reports a fake cancellation, and a null onPressed cannot be
tapped at all. Null stays the default, so every existing host is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CFSGeWxHPY5rHUGrPqhcmG
2026-08-22 19:03:24 +02:00

196 lines
5.9 KiB
Dart

import 'dart:async';
import 'package:crdt_sync/crdt_sync.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sync_settings_ui/sync_settings_ui.dart';
FirebaseRestClient _fakeClient() => FirebaseRestClient(
databaseUrl: 'https://example.firebasedatabase.app',
auth: FirebaseTokenProvider(
apiKey: 'test-key',
store: InMemoryCredentialStore(),
),
);
Widget _wrap(Widget child) => MaterialApp(home: child);
void main() {
group('Firebase section Google sign-in', () {
testWidgets('Google sign-in cancelled shows the cancellation message', (
tester,
) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async => null,
googleAvailable: true,
),
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Sign in with Google'));
await tester.pumpAndSettle();
expect(find.text('Google sign-in was cancelled.'), findsOneWidget);
});
testWidgets(
'Google sign-in that does not persist a session reports the retry '
'message',
(tester) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async => _fakeClient(),
googleAvailable: true,
),
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Sign in with Google'));
await tester.pumpAndSettle();
expect(
find.textContaining('did not save the session'),
findsOneWidget,
);
},
);
testWidgets('Google sign-in wrong-account error surfaces the message', (
tester,
) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async =>
throw FirebaseAuthError('wrong uid'),
googleAvailable: true,
),
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Sign in with Google'));
await tester.pumpAndSettle();
expect(find.text('wrong uid'), findsOneWidget);
});
testWidgets('Google sign-in generic failure surfaces the error text', (
tester,
) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async => throw StateError('boom'),
googleAvailable: true,
),
),
);
await tester.pumpAndSettle();
await tester.tap(find.text('Sign in with Google'));
await tester.pumpAndSettle();
expect(find.textContaining('Google sign-in failed'), findsOneWidget);
});
testWidgets(
'hides the Google button when googleAvailable is true but no '
'googleFirebaseFactory is wired -- a visible button that can never '
'succeed must not render',
(tester) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleAvailable: true,
),
),
);
await tester.pumpAndSettle();
expect(find.text('Sign in with Google'), findsNothing);
},
);
testWidgets('a reason renders a disabled button that explains itself', (
tester,
) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async => _fakeClient(),
googleAvailable: false,
googleUnavailableReason: 'Not available on this platform.',
),
),
);
await tester.pumpAndSettle();
expect(find.text('Sign in with Google'), findsOneWidget);
expect(find.text('Not available on this platform.'), findsOneWidget);
// Disabled, so it cannot report a misleading "cancelled".
final button = tester.widget<FilledButton>(
find.widgetWithText(FilledButton, 'Sign in with Google'),
);
expect(button.onPressed, isNull);
});
testWidgets('no reason keeps the button hidden, as before', (
tester,
) async {
await tester.pumpWidget(
_wrap(
SyncSettingsScreen(
accountLoader: () async => null,
accountSaver: (_) async {},
accountClearer: () async {},
sessionProbe: () async => false,
firebaseFactory: () async => null,
googleFirebaseFactory: () async => _fakeClient(),
googleAvailable: false,
),
),
);
await tester.pumpAndSettle();
expect(find.text('Sign in with Google'), findsNothing);
});
});
}