fix(ui): refine create task panel behavior

This commit is contained in:
Levi committed 2026-09-06 22:17:26 +08:00
1 parent 53d13dfe28
commit ecfd07ccb0
27 files changed
+170 -36

No files matched your search

@@ -9,6 +9,8 @@ import 'package:gopeed/api/model/create_task.dart';
import 'package:gopeed/api/model/downloader_config.dart';
import 'package:gopeed/api/model/options.dart';
import 'package:gopeed/api/model/request.dart';
import 'package:gopeed/api/model/resolve_result.dart';
import 'package:gopeed/api/model/resource.dart';
import 'package:gopeed/core/capabilities/app_capabilities.dart';
import 'package:gopeed/core/capabilities/capability_rpc.dart';
import 'package:gopeed/core/capabilities/gopeed_capability.dart';
@@ -18,6 +20,7 @@ import 'package:gopeed/shared/services/download_directory_picker.dart';
import 'package:gopeed/shared/theme/app_component_themes.dart';
import 'package:gopeed/shared/theme/app_design_tokens.dart';
import 'package:gopeed/shared/theme/app_theme.dart';
import 'package:gopeed/shared/widgets/app_loading_button.dart';
import 'package:gopeed/shared/widgets/app_tooltip.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
@@ -312,6 +315,56 @@ void main() {
expect(options.archivePassword, 'archive-password');
expect(options.deleteAfterExtract, isTrue);
expect(submitted?.opts?.asDefaultPath, isTrue);
expect(submitted?.opts?.name, isEmpty);
});
testWidgets('resolved task keeps rename empty and uses a single-line intrinsic create action', (
WidgetTester tester,
) async {
tester.view.physicalSize = const Size(700, 700);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
final registry = CapabilityRegistry(createAppCapabilityCodecs())
..bind(GopeedMethods.getConfig, (_) => DownloaderConfig(downloadDir: '/downloads'))
..bind(
GopeedMethods.resolve,
(_) => ResolveResult(
id: 'resolved-id',
res: Resource(
name: 'server-generated-name',
files: [FileInfo(name: 'video.mp4', size: 1024)],
),
),
)
..bind(StorageMethods.saveCreateHistory, (_) => const RpcUnit());
final capabilities = AppCapabilities(LocalCapabilityInvoker(registry));
await tester.pumpWidget(
ProviderScope(
overrides: [appCapabilitiesProvider.overrideWithValue(capabilities)],
child: shad.ShadcnApp(
theme: AppTheme.light(),
materialTheme: AppTheme.materialLight(),
home: AppComponentThemes(child: const CreateTaskWindowPage()),
),
),
);
await tester.pumpAndSettle();
await tester.enterText(find.byKey(const ValueKey('create-task-url-input')), 'https://example.com/video.mp4');
await tester.tap(find.byKey(const ValueKey('create-task-confirm-button')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 300));
expect(_fieldText(tester, 'create-task-rename-input'), isEmpty);
final createButton = tester.widget<AppLoadingButton>(find.byKey(const ValueKey('resolve-create-button')));
expect(createButton.child, isA<Text>());
final createText = createButton.child as Text;
expect(createText.data, 'Create');
expect(createText.maxLines, 1);
expect(createText.softWrap, isFalse);
expect(tester.takeException(), isNull);
});
testWidgets('create history supports filtering, deleting one entry, and clearing all entries', (
@@ -134,7 +134,10 @@ void main() {
of: find.byKey(const ValueKey('text-only-loading-button')),
matching: find.byType(shad.CircularProgressIndicator),
);
expect(find.text('Login'), findsNothing);
final hiddenLabel = find.text('Login');
expect(hiddenLabel, findsOneWidget);
expect(tester.widget<Opacity>(find.ancestor(of: hiddenLabel, matching: find.byType(Opacity))).opacity, 0);
expect(find.ancestor(of: hiddenLabel, matching: find.byType(ExcludeSemantics)), findsOneWidget);
expect(loader, findsOneWidget);
expect(tester.getRect(loader).center, bounds.center);
});
+56
View File
@@ -1860,6 +1860,40 @@ void main() {
expect(tester.takeException(), isNull);
});
testWidgets('loading button preserves its content dimensions', (WidgetTester tester) async {
var loading = false;
late StateSetter setButtonState;
await tester.pumpWidget(
shad.ShadcnApp(
theme: AppTheme.light(),
materialTheme: AppTheme.materialLight(),
home: Center(
child: StatefulBuilder(
builder: (context, update) {
setButtonState = update;
return AppLoadingButton(
key: const ValueKey('dimension-stable-loading-button'),
onPressed: () {},
loading: loading,
variant: AppLoadingButtonVariant.primary,
child: const Text('Confirm'),
);
},
),
),
),
);
await tester.pump();
final button = find.byKey(const ValueKey('dimension-stable-loading-button'));
final idleSize = tester.getSize(button);
setButtonState(() => loading = true);
await tester.pump();
expect(tester.getSize(button), idleSize);
expect(find.descendant(of: button, matching: find.byType(shad.CircularProgressIndicator)), findsOneWidget);
});
testWidgets('failed tracker update does not reload or reset the settings page', (WidgetTester tester) async {
await _setTestSize(tester, const Size(1024, 900));
final runtimeController = FailingTrackerRuntimeController();
@@ -2404,6 +2438,27 @@ void main() {
expect(tester.takeException(), isNull);
});
testWidgets('create task rename stays user-controlled and resets when the URL changes', (WidgetTester tester) async {
await _setTestSize(tester, const Size(700, 500));
await tester.pumpWidget(const ProviderScope(child: _CreateTaskPageHarness()));
await tester.pump();
final urlInput = find.byKey(const ValueKey('create-task-url-input'));
final renameInput = find.byKey(const ValueKey('create-task-rename-input'));
await tester.enterText(urlInput, 'https://example.com/first.zip');
await tester.enterText(renameInput, 'custom-name.zip');
expect(find.text('custom-name.zip'), findsOneWidget);
await tester.enterText(urlInput, 'https://example.com/second.zip');
await tester.pump();
final renameField = tester.widget<AppTextField>(
find.descendant(of: renameInput, matching: find.byType(AppTextField)),
);
expect(renameField.controller!.text, isEmpty);
await tester.pump(const Duration(seconds: 1));
});
testWidgets('create task labels direct download without a mode field', (WidgetTester tester) async {
await _setTestSize(tester, const Size(700, 500));
await tester.pumpWidget(const ProviderScope(child: _CreateTaskPageHarness()));
@@ -2591,6 +2646,7 @@ void main() {
expect(treeFinder, findsOneWidget);
final tree = tester.widget<VirtualTreeView<dynamic>>(treeFinder);
expect(tree.branchLine, same(shad.BranchLine.path));
expect(tree.rowHeight, 36);
expect(
tester.widgetList<shad.Checkbox>(find.byType(shad.Checkbox)).every((checkbox) => checkbox.size == null),
isTrue,