Compare commits

...
Author SHA1 Message Date
Thomas SharedInboxandClaude Sonnet 4.6 b489ec6694 fix: disable Try connection button when no password is available (#235)
When no password is stored and no password has been typed, the Try
connection button is now disabled, preventing the crash path entirely
rather than relying solely on after-the-fact form validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 14:12:31 +02:00
2 changed files with 40 additions and 5 deletions
+5 -1
View File
@@ -51,6 +51,7 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
_smtpHostCtrl.addListener(_rebuild); _smtpHostCtrl.addListener(_rebuild);
_sieveHostCtrl.addListener(_rebuild); _sieveHostCtrl.addListener(_rebuild);
_imapHostCtrl.addListener(_rebuild); _imapHostCtrl.addListener(_rebuild);
_passwordCtrl.addListener(_rebuild);
unawaited(_load()); unawaited(_load());
} }
@@ -90,6 +91,7 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
_smtpHostCtrl.removeListener(_rebuild); _smtpHostCtrl.removeListener(_rebuild);
_sieveHostCtrl.removeListener(_rebuild); _sieveHostCtrl.removeListener(_rebuild);
_imapHostCtrl.removeListener(_rebuild); _imapHostCtrl.removeListener(_rebuild);
_passwordCtrl.removeListener(_rebuild);
for (final c in [ for (final c in [
_displayNameCtrl, _displayNameCtrl,
_usernameCtrl, _usernameCtrl,
@@ -353,7 +355,9 @@ class _EditAccountScreenState extends ConsumerState<EditAccountScreen> {
testing: _tryTesting, testing: _tryTesting,
okMessage: _tryOk, okMessage: _tryOk,
errorMessage: _tryErr, errorMessage: _tryErr,
onPressed: _tryConnection, onPressed: _hasStoredPassword || _passwordCtrl.text.isNotEmpty
? _tryConnection
: null,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
FilledButton(onPressed: _save, child: const Text('Save')), FilledButton(onPressed: _save, child: const Text('Save')),
+35 -4
View File
@@ -106,7 +106,8 @@ void main() {
}); });
testWidgets( testWidgets(
'try connection shows password required when no password stored', ( 'try connection button is disabled when no password stored or entered',
(
tester, tester,
) async { ) async {
tester.view.physicalSize = const Size(800, 1400); tester.view.physicalSize = const Size(800, 1400);
@@ -125,11 +126,41 @@ void main() {
); );
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('editTryConnectionButton'))); final button = tester.widget<OutlinedButton>(
find.byKey(const Key('editTryConnectionButton')),
);
expect(button.onPressed, isNull);
});
testWidgets(
'try connection button is enabled after typing password with no stored password',
(tester) async {
tester.view.physicalSize = const Size(800, 1400);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(
buildApp(
initialLocation: '/accounts/acc-1/edit',
overrides: baseOverrides(
accounts: [kTestAccount],
hasStoredPassword: false,
),
),
);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// App must not crash; password field shows a validation error. await tester.enterText(
expect(find.text('Required'), findsOneWidget); find.byKey(const Key('editPasswordField')),
'mypassword',
);
await tester.pump();
final button = tester.widget<OutlinedButton>(
find.byKey(const Key('editTryConnectionButton')),
);
expect(button.onPressed, isNotNull);
}); });
testWidgets('connection error shows error message', (tester) async { testWidgets('connection error shows error message', (tester) async {