mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-14 04:13:43 +00:00
refactor(ui): refine advanced options and settings feedback (#1661)
This commit is contained in:
30 files changed
+243
-68
No files matched your search
@@ -99,6 +99,43 @@ void main() {
|
||||
expect(saved.saving, isFalse);
|
||||
});
|
||||
|
||||
for (final field in ['port', 'DHT', 'PEX']) {
|
||||
test('$field restart notice follows successful persisted changes only', () async {
|
||||
var backend = DownloaderConfig();
|
||||
var failNextSave = false;
|
||||
final registry = CapabilityRegistry(createAppCapabilityCodecs())
|
||||
..bind(GopeedMethods.getConfig, (_) => DownloaderConfig.fromJson(backend.toJson()))
|
||||
..bind(GopeedMethods.putConfig, (config) {
|
||||
if (failNextSave) {
|
||||
failNextSave = false;
|
||||
throw StateError('offline');
|
||||
}
|
||||
backend = DownloaderConfig.fromJson(config.toJson());
|
||||
return const RpcUnit();
|
||||
});
|
||||
final container = _settingsContainer(registry);
|
||||
addTearDown(container.dispose);
|
||||
final initial = await container.read(settingsControllerProvider.future);
|
||||
final controller = container.read(settingsControllerProvider.notifier);
|
||||
final draft = DownloaderConfig.fromJson(initial.config.toJson());
|
||||
switch (field) {
|
||||
case 'port':
|
||||
draft.protocolConfig.bt.listenPort = 6881;
|
||||
case 'DHT':
|
||||
draft.protocolConfig.bt.disableDht = true;
|
||||
case 'PEX':
|
||||
draft.protocolConfig.bt.disablePex = true;
|
||||
}
|
||||
failNextSave = true;
|
||||
await expectLater(controller.save(draft), throwsStateError);
|
||||
expect((await controller.save(draft)).btRestartRequired, isTrue);
|
||||
expect((await controller.save(draft)).btRestartRequired, isFalse);
|
||||
draft.protocolConfig.bt.seedRatio = 2;
|
||||
expect((await controller.save(draft)).btRestartRequired, isFalse);
|
||||
expect((await controller.save(initial.config)).btRestartRequired, isTrue);
|
||||
});
|
||||
}
|
||||
|
||||
test('rapid full config saves run in order and keep the latest user edits', () async {
|
||||
final backend = DownloaderConfig();
|
||||
final entered = Completer<void>();
|
||||
@@ -127,7 +164,8 @@ void main() {
|
||||
final savingSecond = controller.save(second);
|
||||
expect(puts.length, 1);
|
||||
release.complete();
|
||||
await Future.wait([savingFirst, savingSecond]);
|
||||
final results = await Future.wait([savingFirst, savingSecond]);
|
||||
expect(results.map((result) => result.btRestartRequired), [true, false]);
|
||||
expect(puts, hasLength(2));
|
||||
expect(puts[0].protocolConfig.bt.listenPort, 6881);
|
||||
expect(puts[1].protocolConfig.bt.listenPort, 6881);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gopeed/features/tasks/presentation/widgets/create_task_advanced_toggle.dart';
|
||||
import 'package:gopeed/shared/theme/app_component_themes.dart';
|
||||
import 'package:gopeed/shared/theme/app_theme.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
void main() {
|
||||
for (final width in [360.0, 768.0, 1024.0]) {
|
||||
testWidgets('advanced disclosure supports keyboard and touch at width $width', (tester) async {
|
||||
tester.view.physicalSize = Size(width, 768);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
var expanded = false;
|
||||
await tester.pumpWidget(
|
||||
shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: AppComponentThemes(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: CreateTaskAdvancedToggle(
|
||||
expanded: expanded,
|
||||
onPressed: () => setState(() => expanded = !expanded),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.getSize(find.byType(shad.GhostButton)).height, greaterThanOrEqualTo(44));
|
||||
expect(
|
||||
find.byWidgetPredicate((widget) => widget is Semantics && widget.properties.expanded == false),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
|
||||
await tester.pump();
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pumpAndSettle();
|
||||
expect(expanded, isTrue);
|
||||
expect(
|
||||
find.byWidgetPredicate((widget) => widget is Semantics && widget.properties.expanded == true),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.tap(find.text('Advanced'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(expanded, isFalse);
|
||||
expect(tester.takeException(), isNull);
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1636,6 +1636,46 @@ void main() {
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('BT settings show a restart message only after an affected auto-save', (tester) async {
|
||||
await _setTestSize(tester, const Size(1024, 900));
|
||||
final controller = FakeSettingsController()..btRestartRequired = true;
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
appRuntimeControllerProvider.overrideWith(FakeRuntimeController.new),
|
||||
appPlatformControllerProvider.overrideWith(FakePlatformController.new),
|
||||
settingsControllerProvider.overrideWith(() => controller),
|
||||
],
|
||||
child: const _SettingsTestApp(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Downloads'));
|
||||
await tester.pumpAndSettle();
|
||||
const message = 'This setting takes effect after restarting the app';
|
||||
expect(find.text(message), findsNothing);
|
||||
final port = find.byKey(const ValueKey('bt-listen-port-input'));
|
||||
await tester.ensureVisible(port);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(port, '6881');
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
expect(find.text(message), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
expect(find.text(message), findsOneWidget);
|
||||
expect(tester.widget<AppToastContent>(find.byType(AppToastContent)).type, AppToastType.info);
|
||||
await tester.pump(const Duration(seconds: 6));
|
||||
await tester.pumpAndSettle();
|
||||
controller.btRestartRequired = false;
|
||||
final seedRatio = find.byKey(const ValueKey('bt-seed-ratio-input'));
|
||||
await tester.ensureVisible(seedRatio);
|
||||
await tester.enterText(seedRatio, '2');
|
||||
await tester.pump(const Duration(milliseconds: 700));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(message), findsNothing);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('settings FTP connections save into the FTP protocol config', (WidgetTester tester) async {
|
||||
await _setTestSize(tester, const Size(1024, 900));
|
||||
final settingsController = FakeSettingsController();
|
||||
@@ -6181,12 +6221,15 @@ ApiServerState _testApiServerState({
|
||||
}
|
||||
|
||||
class FakeSettingsController extends SettingsController {
|
||||
bool btRestartRequired = false;
|
||||
|
||||
@override
|
||||
Future<SettingsState> build() async => SettingsState(config: DownloaderConfig());
|
||||
|
||||
@override
|
||||
Future<void> save(DownloaderConfig config) async {
|
||||
Future<SettingsSaveResult> save(DownloaderConfig config) async {
|
||||
state = AsyncValue.data(SettingsState(config: config));
|
||||
return SettingsSaveResult(btRestartRequired: btRestartRequired);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user