diff --git a/lib/web/web_settings_page_gateway.dart b/lib/web/web_settings_page_gateway.dart index 36bd7a0d..89d68223 100644 --- a/lib/web/web_settings_page_gateway.dart +++ b/lib/web/web_settings_page_gateway.dart @@ -153,9 +153,9 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { () => directMessageInternal = result.message, ); }, - child: Text(appText('测试连接', 'Test')), + child: Text(appText('测试连接', 'Test Connection')), ), - FilledButton.icon( + FilledButton( onPressed: controller.aiGatewayBusy ? null : () async { @@ -195,7 +195,7 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { ); } }, - icon: controller.aiGatewayBusy + child: controller.aiGatewayBusy ? const SizedBox( width: 14, height: 14, @@ -203,8 +203,7 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { strokeWidth: 2, ), ) - : const Icon(Icons.play_circle_outline_rounded), - label: Text(appText('保存并生效', 'Save & apply')), + : Text(appText('保存并生效', 'Save & apply')), ), ], ), @@ -263,6 +262,7 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { const SizedBox(height: 16), ...controller.settingsDraft.externalAcpEndpoints.map( (profile) => Padding( + key: ValueKey('web-external-acp-card-${profile.providerKey}'), padding: const EdgeInsets.only(bottom: 12), child: buildExternalAcpProviderCardInternal( context, @@ -335,19 +335,21 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { ), const SizedBox(height: 12), TextField( + key: ValueKey('web-external-acp-label-${profile.providerKey}'), controller: labelController, decoration: InputDecoration( labelText: appText('显示名称', 'Display name'), ), - onChanged: (_) => stageExternalAcpDraftInternal(controller), + onChanged: (_) => setStateInternal(() {}), ), const SizedBox(height: 12), TextField( + key: ValueKey('web-external-acp-endpoint-${profile.providerKey}'), controller: endpointController, decoration: InputDecoration( labelText: appText('ACP Server Endpoint', 'ACP Server Endpoint'), ), - onChanged: (_) => stageExternalAcpDraftInternal(controller), + onChanged: (_) => setStateInternal(() {}), ), const SizedBox(height: 8), Text( @@ -652,9 +654,9 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { '${result.state.toUpperCase()} · ${result.message}', ); }, - child: Text(appText('Test', 'Test')), + child: Text(appText('测试连接', 'Test Connection')), ), - FilledButton.icon( + FilledButton( onPressed: controller.relayBusy ? null : () async { @@ -686,14 +688,13 @@ extension WebSettingsPageGatewayMixinInternal on WebSettingsPageStateInternal { onMessageChanged('$error'); } }, - icon: controller.relayBusy + child: controller.relayBusy ? const SizedBox( width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2), ) - : const Icon(Icons.play_circle_outline_rounded), - label: Text(appText('保存并生效', 'Save & apply')), + : Text(appText('保存并生效', 'Save & apply')), ), ], ), diff --git a/test/features/web_settings_page_external_acp_suite.dart b/test/features/web_settings_page_external_acp_suite.dart new file mode 100644 index 00000000..301694a1 --- /dev/null +++ b/test/features/web_settings_page_external_acp_suite.dart @@ -0,0 +1,65 @@ +@TestOn('vm') +library; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:xworkmate/app/app_controller_web.dart' as web_app; +import 'package:xworkmate/models/app_models.dart'; +import 'package:xworkmate/runtime/runtime_models.dart'; +import 'package:xworkmate/web/web_settings_page_core.dart'; + +import '../test_support.dart'; + +void main() { + testWidgets('Web external ACP editor supports continuous input', ( + WidgetTester tester, + ) async { + SharedPreferences.setMockInitialValues({}); + final controller = web_app.AppController(); + addTearDown(controller.dispose); + await tester.pump(const Duration(milliseconds: 100)); + await tester.pumpAndSettle(); + final customProfile = buildCustomExternalAcpEndpointProfile( + controller.settingsDraft.externalAcpEndpoints, + label: 'Initial Name', + endpoint: 'wss://initial.example.com/acp', + ); + await controller.saveSettingsDraft( + controller.settingsDraft.copyWith( + externalAcpEndpoints: [ + ...controller.settingsDraft.externalAcpEndpoints, + customProfile, + ], + ), + ); + controller.setSettingsTab(SettingsTab.gateway); + + await pumpPage( + tester, + child: SizedBox( + width: 1280, + height: 960, + child: WebSettingsPage(controller: controller, showSectionTabs: false), + ), + platform: TargetPlatform.macOS, + ); + + final labelField = find.byKey( + ValueKey('web-external-acp-label-${customProfile.providerKey}'), + ); + + expect(labelField, findsOneWidget); + expect(find.text('测试连接'), findsWidgets); + expect(find.text('保存并生效'), findsWidgets); + + await tester.enterText(labelField, 'A'); + await tester.pump(); + await tester.enterText(labelField, 'AB'); + await tester.pump(); + await tester.enterText(labelField, 'ABC'); + await tester.pump(); + + expect(find.text('ABC'), findsOneWidget); + }); +}