From 198f9b8a5e434da5c042f4d9dd77c7843a76a1dc Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 20:40:25 +0300 Subject: [PATCH 1/3] refactor(connections): compose connection forms from reusable network, credentials, and SSL sections Closes #1364 --- .../forms/connection_auth_section.dart | 122 ++++++++++++++++ .../forms/connection_database_section.dart | 38 +++++ .../forms/connection_host_port_section.dart | 68 +++++++++ .../mongodb/mongodb_connection_form.dart | 102 +++----------- lib/features/mysql/mysql_connection_form.dart | 109 +++------------ .../postgresql_connection_form.dart | 132 +++--------------- .../connection_form_sections_test.dart | 115 +++++++++++++++ 7 files changed, 399 insertions(+), 287 deletions(-) create mode 100644 lib/features/connections/forms/connection_auth_section.dart create mode 100644 lib/features/connections/forms/connection_database_section.dart create mode 100644 lib/features/connections/forms/connection_host_port_section.dart create mode 100644 test/features/connections/connection_form_sections_test.dart diff --git a/lib/features/connections/forms/connection_auth_section.dart b/lib/features/connections/forms/connection_auth_section.dart new file mode 100644 index 00000000..bdb4365f --- /dev/null +++ b/lib/features/connections/forms/connection_auth_section.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; +import 'package:querya_desktop/shared/widgets/widgets.dart'; + +/// Reusable Username & Password input section for database connection forms (#1364). +class ConnectionAuthSection extends material.StatefulWidget { + const ConnectionAuthSection({ + super.key, + required this.usernameController, + required this.passwordController, + this.usernamePlaceholder = 'postgres', + this.usernameLabel = 'Username', + this.passwordLabel = 'Password', + this.isEditing = false, + this.removeSavedPassword = false, + this.onRemoveSavedPasswordChanged, + this.useRowLayout = true, + }); + + final material.TextEditingController usernameController; + final material.TextEditingController passwordController; + final String usernamePlaceholder; + final String usernameLabel; + final String passwordLabel; + final bool isEditing; + final bool removeSavedPassword; + final material.ValueChanged? onRemoveSavedPasswordChanged; + final bool useRowLayout; + + @override + material.State createState() => + _ConnectionAuthSectionState(); +} + +class _ConnectionAuthSectionState extends material.State { + bool _showPassword = false; + + material.Widget _buildUsernameField() { + return material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + Text(widget.usernameLabel).small().semiBold(), + const Gap(8), + TextField( + controller: widget.usernameController, + placeholder: Text(widget.usernamePlaceholder), + ), + ], + ); + } + + material.Widget _buildPasswordField() { + return material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + Text(widget.passwordLabel).small().semiBold(), + if (widget.isEditing && widget.onRemoveSavedPasswordChanged != null) + RemoveSavedPasswordOption( + value: widget.removeSavedPassword, + onChanged: widget.onRemoveSavedPasswordChanged!, + ), + const Gap(8), + material.Stack( + children: [ + TextField( + controller: widget.passwordController, + enabled: !widget.removeSavedPassword, + placeholder: Text( + widget.isEditing ? 'Leave blank to keep existing' : 'Password', + ), + obscureText: !_showPassword, + ), + material.Positioned( + right: 8, + top: 0, + bottom: 0, + child: material.Center( + child: material.IconButton( + icon: material.Icon( + _showPassword + ? material.Icons.visibility_off + : material.Icons.visibility, + size: 20, + ), + onPressed: () => setState(() => _showPassword = !_showPassword), + padding: material.EdgeInsets.zero, + constraints: const material.BoxConstraints(), + ), + ), + ), + ], + ), + ], + ); + } + + @override + material.Widget build(material.BuildContext context) { + if (widget.useRowLayout) { + return material.Row( + crossAxisAlignment: material.CrossAxisAlignment.start, + children: [ + material.Expanded(child: _buildUsernameField()), + const Gap(16), + material.Expanded(child: _buildPasswordField()), + ], + ); + } + + return material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + _buildUsernameField(), + const Gap(16), + _buildPasswordField(), + ], + ); + } +} diff --git a/lib/features/connections/forms/connection_database_section.dart b/lib/features/connections/forms/connection_database_section.dart new file mode 100644 index 00000000..afd8ff1b --- /dev/null +++ b/lib/features/connections/forms/connection_database_section.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/shared/widgets/widgets.dart'; + +/// Reusable Database name input field for connection forms (#1364). +class ConnectionDatabaseSection extends StatelessWidget { + const ConnectionDatabaseSection({ + super.key, + required this.databaseController, + this.label = 'Database', + this.placeholder = 'postgres', + this.hint, + }); + + final material.TextEditingController databaseController; + final String label; + final String placeholder; + final String? hint; + + @override + Widget build(BuildContext context) { + return material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + Text(label).small().semiBold(), + if (hint != null) ...[ + const Gap(4), + Text(hint!).muted().small(), + ], + const Gap(8), + TextField( + controller: databaseController, + placeholder: Text(placeholder), + ), + ], + ); + } +} diff --git a/lib/features/connections/forms/connection_host_port_section.dart b/lib/features/connections/forms/connection_host_port_section.dart new file mode 100644 index 00000000..d1ea1929 --- /dev/null +++ b/lib/features/connections/forms/connection_host_port_section.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart' as material; +import 'package:querya_desktop/shared/widgets/widgets.dart'; + +/// Reusable Host & Port input row for database connection forms (#1364). +class ConnectionHostPortSection extends StatelessWidget { + const ConnectionHostPortSection({ + super.key, + required this.hostController, + required this.portController, + this.hostPlaceholder = 'localhost', + this.portPlaceholder = '5432', + this.hostLabel = 'Host', + this.portLabel = 'Port', + this.hostFlex = 3, + this.portFlex = 2, + this.gap = 16, + }); + + final material.TextEditingController hostController; + final material.TextEditingController portController; + final String hostPlaceholder; + final String portPlaceholder; + final String hostLabel; + final String portLabel; + final int hostFlex; + final int portFlex; + final double gap; + + @override + Widget build(BuildContext context) { + return material.Row( + crossAxisAlignment: material.CrossAxisAlignment.start, + children: [ + material.Expanded( + flex: hostFlex, + child: material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + Text(hostLabel).small().semiBold(), + const Gap(8), + TextField( + controller: hostController, + placeholder: Text(hostPlaceholder), + ), + ], + ), + ), + Gap(gap), + material.Expanded( + flex: portFlex, + child: material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + mainAxisSize: material.MainAxisSize.min, + children: [ + Text(portLabel).small().semiBold(), + const Gap(8), + TextField( + controller: portController, + placeholder: Text(portPlaceholder), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/features/mongodb/mongodb_connection_form.dart b/lib/features/mongodb/mongodb_connection_form.dart index a0f60dd2..4a13b9e4 100644 --- a/lib/features/mongodb/mongodb_connection_form.dart +++ b/lib/features/mongodb/mongodb_connection_form.dart @@ -12,7 +12,9 @@ import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; -import 'package:querya_desktop/features/connections/ssl_certificate_support.dart'; +import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; import 'package:querya_desktop/shared/widgets/form_validity_notifier.dart'; import 'package:querya_desktop/shared/widgets/ssl_certificate_fields.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; @@ -101,7 +103,6 @@ class _MongoConnectionFormContentState bool _useConnectionString = false; bool _useSSL = false; bool _removeSavedPassword = false; - bool _showPassword = false; bool _isTesting = false; String? _testResult; Timer? _dismissTimer; @@ -472,92 +473,23 @@ class _MongoConnectionFormContentState ), const Gap(16), // Host and Port - material.Row( - children: [ - material.Expanded( - flex: 3, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - mainAxisSize: material.MainAxisSize.min, - children: [ - const Text('Host').small().semiBold(), - const Gap(8), - TextField( - controller: _hostController, - placeholder: const Text('localhost'), - ), - ], - ), - ), - const Gap(12), - material.Expanded( - flex: 1, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - mainAxisSize: material.MainAxisSize.min, - children: [ - const Text('Port').small().semiBold(), - const Gap(8), - TextField( - controller: _portController, - placeholder: const Text('27017'), - ), - ], - ), - ), - ], + ConnectionHostPortSection( + hostController: _hostController, + portController: _portController, + defaultPort: '27017', ), const Gap(16), // Authentication - const Text('Authentication (Optional)') - .small() - .semiBold(), - const Gap(8), - TextField( - controller: _usernameController, - placeholder: const Text('Username'), - ), - const Gap(12), - if (_isEditing) - RemoveSavedPasswordOption( - value: _removeSavedPassword, - onChanged: (v) => - setState(() => _removeSavedPassword = v), - ), - material.Stack( - children: [ - TextField( - controller: _passwordController, - enabled: !_removeSavedPassword, - placeholder: Text( - _isEditing - ? 'Leave blank to keep existing' - : 'Password', - ), - obscureText: !_showPassword, - ), - material.Positioned( - right: 8, - top: 0, - bottom: 0, - child: material.Center( - child: material.IconButton( - icon: material.Icon( - _showPassword - ? material.Icons.visibility_off - : material.Icons.visibility, - size: 20, - ), - onPressed: () => setState( - () => _showPassword = !_showPassword), - padding: material.EdgeInsets.zero, - constraints: const material.BoxConstraints(), - ), - ), - ), - ], + ConnectionAuthSection( + usernameController: _usernameController, + passwordController: _passwordController, + usernameLabel: 'Authentication (Optional)', + usernamePlaceholder: 'Username', + isEditing: _isEditing, + removeSavedPassword: _removeSavedPassword, + onRemoveSavedPasswordChanged: (v) => + setState(() => _removeSavedPassword = v), + useRowLayout: false, ), const Gap(16), // Database and Auth Source diff --git a/lib/features/mysql/mysql_connection_form.dart b/lib/features/mysql/mysql_connection_form.dart index f2edf644..c3b9a901 100644 --- a/lib/features/mysql/mysql_connection_form.dart +++ b/lib/features/mysql/mysql_connection_form.dart @@ -9,6 +9,9 @@ import 'package:querya_desktop/core/database/mysql_connection.dart'; import 'package:querya_desktop/core/layout/window_layout.dart'; import 'package:querya_desktop/core/security/ssh_tunnel_config.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; +import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; @@ -67,7 +70,6 @@ class _MysqlConnectionFormContentState bool _removeSavedPassword = false; bool _useSSL = true; - bool _showPassword = false; bool _isTesting = false; String? _testResult; Timer? _dismissTimer; @@ -417,98 +419,27 @@ class _MysqlConnectionFormContentState ), ), const Gap(16), - material.Row( - children: [ - material.Expanded( - flex: 3, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - mainAxisSize: material.MainAxisSize.min, - children: [ - const Text('Host').small().semiBold(), - const Gap(8), - TextField( - controller: _hostController, - placeholder: const Text('localhost'), - ), - ], - ), - ), - const Gap(12), - material.Expanded( - flex: 1, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - mainAxisSize: material.MainAxisSize.min, - children: [ - const Text('Port').small().semiBold(), - const Gap(8), - TextField( - controller: _portController, - placeholder: const Text('3306'), - ), - ], - ), - ), - ], - ), - const Gap(16), - const Text('Database (optional)').small().semiBold(), - const Gap(8), - TextField( - controller: _databaseController, - placeholder: const Text('Leave empty to browse all'), + ConnectionHostPortSection( + hostController: _hostController, + portController: _portController, + defaultPort: '3306', ), const Gap(16), - const Text('Username').small().semiBold(), - const Gap(8), - TextField( - controller: _usernameController, - placeholder: const Text('root'), + ConnectionDatabaseSection( + databaseController: _databaseController, + label: 'Database (optional)', + placeholder: 'Leave empty to browse all', ), const Gap(16), - const Text('Password').small().semiBold(), - if (_isEditing) - RemoveSavedPasswordOption( - value: _removeSavedPassword, - onChanged: (v) => - setState(() => _removeSavedPassword = v), - ), - const Gap(8), - material.Stack( - children: [ - TextField( - controller: _passwordController, - enabled: !_removeSavedPassword, - placeholder: Text( - _isEditing - ? 'Leave blank to keep existing' - : 'Password', - ), - obscureText: !_showPassword, - ), - material.Positioned( - right: 8, - top: 0, - bottom: 0, - child: material.Center( - child: material.IconButton( - icon: material.Icon( - _showPassword - ? material.Icons.visibility_off - : material.Icons.visibility, - size: 20, - ), - onPressed: () => setState( - () => _showPassword = !_showPassword), - padding: material.EdgeInsets.zero, - constraints: const material.BoxConstraints(), - ), - ), - ), - ], + ConnectionAuthSection( + usernameController: _usernameController, + passwordController: _passwordController, + defaultUsername: 'root', + isEditing: _isEditing, + removeSavedPassword: _removeSavedPassword, + onRemoveSavedPasswordChanged: (v) => + setState(() => _removeSavedPassword = v), + useRowLayout: false, ), const Gap(16), material.Row( diff --git a/lib/features/postgresql/postgresql_connection_form.dart b/lib/features/postgresql/postgresql_connection_form.dart index 91619704..98e32316 100644 --- a/lib/features/postgresql/postgresql_connection_form.dart +++ b/lib/features/postgresql/postgresql_connection_form.dart @@ -12,7 +12,9 @@ import 'package:querya_desktop/core/security/ssh_tunnel_config.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; -import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; import 'package:querya_desktop/shared/widgets/form_validity_notifier.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; @@ -66,7 +68,6 @@ class _PostgresConnectionFormContentState bool _useSSL = false; bool _removeSavedPassword = false; - bool _showPassword = false; bool _isTesting = false; String? _testResult; Timer? _dismissTimer; @@ -537,121 +538,26 @@ class _PostgresConnectionFormContentState ), ), const Gap(16), - // Host and Port Row - material.Row( - crossAxisAlignment: material.CrossAxisAlignment.start, - children: [ - material.Expanded( - flex: 3, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - children: [ - const Text('Host').small().semiBold(), - const Gap(8), - TextField( - controller: _hostController, - placeholder: const Text('localhost'), - ), - ], - ), - ), - const Gap(16), - material.Expanded( - flex: 2, - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - children: [ - const Text('Port').small().semiBold(), - const Gap(8), - TextField( - controller: _portController, - placeholder: const Text('5432'), - ), - ], - ), - ), - ], + ConnectionHostPortSection( + hostController: _hostController, + portController: _portController, + hostPlaceholder: 'localhost', + portPlaceholder: '5432', ), const Gap(16), - // Database - const Text('Database').small().semiBold(), - const Gap(8), - TextField( - controller: _databaseController, - placeholder: const Text('postgres'), + ConnectionDatabaseSection( + databaseController: _databaseController, + placeholder: 'postgres', ), const Gap(16), - // Username and Password Row - material.Row( - crossAxisAlignment: material.CrossAxisAlignment.start, - children: [ - material.Expanded( - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - children: [ - const Text('Username').small().semiBold(), - const Gap(8), - TextField( - controller: _usernameController, - placeholder: const Text('postgres'), - ), - ], - ), - ), - const Gap(16), - material.Expanded( - child: material.Column( - crossAxisAlignment: - material.CrossAxisAlignment.stretch, - children: [ - const Text('Password').small().semiBold(), - if (_isEditing) - RemoveSavedPasswordOption( - value: _removeSavedPassword, - onChanged: (v) => setState( - () => _removeSavedPassword = v), - ), - const Gap(8), - material.Stack( - children: [ - TextField( - controller: _passwordController, - enabled: !_removeSavedPassword, - placeholder: Text( - _isEditing - ? 'Leave blank to keep existing' - : 'Password', - ), - obscureText: !_showPassword, - ), - material.Positioned( - right: 8, - top: 0, - bottom: 0, - child: material.Center( - child: material.IconButton( - icon: material.Icon( - _showPassword - ? material.Icons.visibility_off - : material.Icons.visibility, - size: 20, - ), - onPressed: () => setState( - () => _showPassword = !_showPassword), - padding: material.EdgeInsets.zero, - constraints: const material.BoxConstraints(), - ), - ), - ), - ], - ), - ], - ), - ), - ], + ConnectionAuthSection( + usernameController: _usernameController, + passwordController: _passwordController, + usernamePlaceholder: 'postgres', + isEditing: _isEditing, + removeSavedPassword: _removeSavedPassword, + onRemoveSavedPasswordChanged: (v) => + setState(() => _removeSavedPassword = v), ), // SSL/TLS Toggle material.Row( diff --git a/test/features/connections/connection_form_sections_test.dart b/test/features/connections/connection_form_sections_test.dart new file mode 100644 index 00000000..91c4aa6e --- /dev/null +++ b/test/features/connections/connection_form_sections_test.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart' as material; +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/theme/app_theme.dart'; +import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; +import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; + +void main() { + Widget buildApp(Widget child) { + return ShadcnApp( + theme: AppTheme.dark, + darkTheme: AppTheme.dark, + themeMode: ThemeMode.dark, + home: material.Scaffold( + body: material.SingleChildScrollView( + child: child, + ), + ), + ); + } + + group('ConnectionHostPortSection', () { + testWidgets('renders Host and Port fields with custom labels and values', + (tester) async { + final hostController = material.TextEditingController(text: '127.0.0.1'); + final portController = material.TextEditingController(text: '5432'); + + await tester.pumpWidget( + buildApp( + ConnectionHostPortSection( + hostController: hostController, + portController: portController, + defaultPort: '5432', + ), + ), + ); + + expect(find.text('Host'), findsOneWidget); + expect(find.text('Port'), findsOneWidget); + expect(find.text('127.0.0.1'), findsOneWidget); + expect(find.text('5432'), findsOneWidget); + }); + }); + + group('ConnectionDatabaseSection', () { + testWidgets('renders database field with label and placeholder', + (tester) async { + final dbController = material.TextEditingController(text: 'my_db'); + + await tester.pumpWidget( + buildApp( + ConnectionDatabaseSection( + databaseController: dbController, + label: 'Database name', + placeholder: 'postgres', + ), + ), + ); + + expect(find.text('Database name'), findsOneWidget); + expect(find.text('my_db'), findsOneWidget); + }); + }); + + group('ConnectionAuthSection', () { + testWidgets('toggles password visibility when eye icon is clicked', + (tester) async { + final userController = material.TextEditingController(text: 'admin'); + final passController = material.TextEditingController(text: 'secret'); + + await tester.pumpWidget( + buildApp( + ConnectionAuthSection( + usernameController: userController, + passwordController: passController, + useRowLayout: false, + ), + ), + ); + + expect(find.text('Username'), findsOneWidget); + expect(find.text('Password'), findsOneWidget); + expect(find.byIcon(material.Icons.visibility), findsOneWidget); + + await tester.tap(find.byIcon(material.Icons.visibility)); + await tester.pumpAndSettle(); + + expect(find.byIcon(material.Icons.visibility_off), findsOneWidget); + }); + + testWidgets('renders in row layout and shows remove saved password when editing', + (tester) async { + final userController = material.TextEditingController(text: 'admin'); + final passController = material.TextEditingController(); + bool removePassword = false; + + await tester.pumpWidget( + buildApp( + ConnectionAuthSection( + usernameController: userController, + passwordController: passController, + isEditing: true, + removeSavedPassword: removePassword, + onRemoveSavedPasswordChanged: (v) => removePassword = v, + useRowLayout: true, + ), + ), + ); + + expect(find.text('Remove saved password on save'), findsOneWidget); + expect(find.text('Leave blank to keep existing'), findsOneWidget); + }); + }); +} From 60d72dc0ba34bcef57bb86d9e384a56b770b2f16 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 21:02:50 +0300 Subject: [PATCH 2/3] fix(connections): align form section API and restore imports - use portPlaceholder/usernamePlaceholder (the sections' real parameters) instead of nonexistent defaultPort/defaultUsername in mongo, mysql and the sections test - restore ssl_certificate_support import in the mongo form and the ssh_tunnel_section import in the postgres form - drop unused imports; keep mongo's 3:1 host/port layout --- lib/features/mongodb/mongodb_connection_form.dart | 7 ++++--- lib/features/mysql/mysql_connection_form.dart | 5 ++--- lib/features/postgresql/postgresql_connection_form.dart | 2 +- .../connections/connection_form_sections_test.dart | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/features/mongodb/mongodb_connection_form.dart b/lib/features/mongodb/mongodb_connection_form.dart index 4a13b9e4..8508e334 100644 --- a/lib/features/mongodb/mongodb_connection_form.dart +++ b/lib/features/mongodb/mongodb_connection_form.dart @@ -9,12 +9,11 @@ import 'package:querya_desktop/core/database/mongodb_connection.dart'; import 'package:querya_desktop/core/layout/window_layout.dart'; import 'package:querya_desktop/core/security/ssh_tunnel_config.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; -import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; -import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; +import 'package:querya_desktop/features/connections/ssl_certificate_support.dart'; import 'package:querya_desktop/shared/widgets/form_validity_notifier.dart'; import 'package:querya_desktop/shared/widgets/ssl_certificate_fields.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; @@ -476,7 +475,9 @@ class _MongoConnectionFormContentState ConnectionHostPortSection( hostController: _hostController, portController: _portController, - defaultPort: '27017', + portPlaceholder: '27017', + portFlex: 1, + gap: 12, ), const Gap(16), // Authentication diff --git a/lib/features/mysql/mysql_connection_form.dart b/lib/features/mysql/mysql_connection_form.dart index c3b9a901..271115da 100644 --- a/lib/features/mysql/mysql_connection_form.dart +++ b/lib/features/mysql/mysql_connection_form.dart @@ -12,7 +12,6 @@ import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; import 'package:querya_desktop/features/connections/forms/connection_host_port_section.dart'; -import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; import 'package:querya_desktop/features/connections/ssl_certificate_support.dart'; @@ -422,7 +421,7 @@ class _MysqlConnectionFormContentState ConnectionHostPortSection( hostController: _hostController, portController: _portController, - defaultPort: '3306', + portPlaceholder: '3306', ), const Gap(16), ConnectionDatabaseSection( @@ -434,7 +433,7 @@ class _MysqlConnectionFormContentState ConnectionAuthSection( usernameController: _usernameController, passwordController: _passwordController, - defaultUsername: 'root', + usernamePlaceholder: 'root', isEditing: _isEditing, removeSavedPassword: _removeSavedPassword, onRemoveSavedPasswordChanged: (v) => diff --git a/lib/features/postgresql/postgresql_connection_form.dart b/lib/features/postgresql/postgresql_connection_form.dart index 98e32316..027fa688 100644 --- a/lib/features/postgresql/postgresql_connection_form.dart +++ b/lib/features/postgresql/postgresql_connection_form.dart @@ -10,7 +10,7 @@ import 'package:querya_desktop/core/database/postgres_connection.dart'; import 'package:querya_desktop/core/layout/window_layout.dart'; import 'package:querya_desktop/core/security/ssh_tunnel_config.dart'; import 'package:querya_desktop/core/storage/local_db.dart'; -import 'package:querya_desktop/features/connections/remove_saved_password_option.dart'; +import 'package:querya_desktop/features/connections/ssh_tunnel_section.dart'; import 'package:querya_desktop/features/connections/connection_creation_flow.dart'; import 'package:querya_desktop/features/connections/forms/connection_auth_section.dart'; import 'package:querya_desktop/features/connections/forms/connection_database_section.dart'; diff --git a/test/features/connections/connection_form_sections_test.dart b/test/features/connections/connection_form_sections_test.dart index 91c4aa6e..625960b5 100644 --- a/test/features/connections/connection_form_sections_test.dart +++ b/test/features/connections/connection_form_sections_test.dart @@ -31,7 +31,7 @@ void main() { ConnectionHostPortSection( hostController: hostController, portController: portController, - defaultPort: '5432', + portPlaceholder: '5432', ), ), ); From 379694176da80c197434a8bcb43bdddc3e5dea52 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Sat, 10 Oct 2026 21:11:45 +0300 Subject: [PATCH 3/3] test(connections): fix expectations in connection form sections test - use a port value that differs from the placeholder so the finder is unique - the password label and placeholder both read 'Password' - match the real remove-saved-password label --- .../connections/connection_form_sections_test.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/features/connections/connection_form_sections_test.dart b/test/features/connections/connection_form_sections_test.dart index 625960b5..bdeafe76 100644 --- a/test/features/connections/connection_form_sections_test.dart +++ b/test/features/connections/connection_form_sections_test.dart @@ -24,7 +24,7 @@ void main() { testWidgets('renders Host and Port fields with custom labels and values', (tester) async { final hostController = material.TextEditingController(text: '127.0.0.1'); - final portController = material.TextEditingController(text: '5432'); + final portController = material.TextEditingController(text: '6543'); await tester.pumpWidget( buildApp( @@ -39,7 +39,7 @@ void main() { expect(find.text('Host'), findsOneWidget); expect(find.text('Port'), findsOneWidget); expect(find.text('127.0.0.1'), findsOneWidget); - expect(find.text('5432'), findsOneWidget); + expect(find.text('6543'), findsOneWidget); }); }); @@ -80,7 +80,8 @@ void main() { ); expect(find.text('Username'), findsOneWidget); - expect(find.text('Password'), findsOneWidget); + // Label plus the field's own placeholder. + expect(find.text('Password'), findsNWidgets(2)); expect(find.byIcon(material.Icons.visibility), findsOneWidget); await tester.tap(find.byIcon(material.Icons.visibility)); @@ -108,7 +109,7 @@ void main() { ), ); - expect(find.text('Remove saved password on save'), findsOneWidget); + expect(find.text('Remove the saved password'), findsOneWidget); expect(find.text('Leave blank to keep existing'), findsOneWidget); }); });