mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-13 20:03:46 +00:00
fix: create task panel mobile UI (#1488)
This commit is contained in:
36 files changed
+869
-827
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gopeed/features/tasks/presentation/widgets/resolve_task_actions.dart';
|
||||
import 'package:gopeed/l10n/l10n.dart';
|
||||
import 'package:gopeed/shared/theme/app_component_themes.dart';
|
||||
import 'package:gopeed/shared/theme/app_theme.dart';
|
||||
import 'package:gopeed/shared/widgets/app_loading_button.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
void main() {
|
||||
for (final width in [390.0, 1024.0]) {
|
||||
for (final language in ['en', 'zh', 'de']) {
|
||||
testWidgets('resolve actions stay equal at $width in $language, including loading', (tester) async {
|
||||
tester.view.physicalSize = Size(width, 760);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
var submitting = false;
|
||||
var cancelled = false;
|
||||
await tester.pumpWidget(
|
||||
shad.ShadcnApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: Builder(
|
||||
builder: (context) => Localizations.override(
|
||||
context: context,
|
||||
locale: Locale(language),
|
||||
delegates: const [AppLocalizations.delegate],
|
||||
child: AppComponentThemes(
|
||||
child: Center(
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) => shad.AlertDialog(
|
||||
padding: const EdgeInsets.all(18),
|
||||
content: SizedBox(width: width < 760 ? width - 32 : 720, height: 100),
|
||||
actions: [
|
||||
Flexible(
|
||||
child: ResolveTaskActions(
|
||||
submitting: submitting,
|
||||
onCancel: () => cancelled = true,
|
||||
onCreate: () => setState(() => submitting = true),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
final cancel = find.byKey(const ValueKey('resolve-cancel-button'));
|
||||
final create = find.byKey(const ValueKey('resolve-create-button'));
|
||||
final cancelRect = tester.getRect(cancel);
|
||||
final createRect = tester.getRect(create);
|
||||
expect(cancelRect.size, createRect.size);
|
||||
expect(cancelRect.center.dy, createRect.center.dy);
|
||||
expect(cancelRect.left, greaterThanOrEqualTo(0));
|
||||
expect(createRect.right, lessThanOrEqualTo(width));
|
||||
expect(find.text(appLocalizationsFor(language).createAction), findsOneWidget);
|
||||
await tester.tap(cancel);
|
||||
expect(cancelled, isTrue);
|
||||
await tester.tap(create);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(tester.widget<AppLoadingButton>(create).loading, isTrue);
|
||||
expect(tester.widget<shad.SecondaryButton>(cancel).onPressed, isNull);
|
||||
expect(tester.getRect(cancel), cancelRect);
|
||||
expect(tester.getRect(create), createRect);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,30 @@ import 'package:gopeed/shared/theme/app_theme.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
|
||||
|
||||
void main() {
|
||||
testWidgets('provides the standard text field appearance', (tester) async {
|
||||
late shad.TextFieldTheme textFieldTheme;
|
||||
|
||||
await tester.pumpWidget(
|
||||
shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: AppComponentThemes(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
textFieldTheme = shad.ComponentTheme.of<shad.TextFieldTheme>(context);
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(textFieldTheme.filled, isTrue);
|
||||
expect(textFieldTheme.border, Border.all(color: AppPalette.light.border));
|
||||
expect(textFieldTheme.borderRadius, BorderRadius.circular(AppDesignTokens.controlRadius));
|
||||
expect(textFieldTheme.padding, AppDesignTokens.textFieldPadding);
|
||||
});
|
||||
|
||||
testWidgets('provides the standard checkbox metrics', (tester) async {
|
||||
late shad.CheckboxTheme checkboxTheme;
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -449,7 +449,7 @@ void main() {
|
||||
child: shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: const ExtensionsPage(),
|
||||
home: const AppComponentThemes(child: ExtensionsPage()),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -469,6 +469,7 @@ void main() {
|
||||
final cardRect = tester.getRect(find.byKey(const ValueKey('extension-card-extension-0')));
|
||||
final rightmostCardRect = tester.getRect(find.byKey(const ValueKey('extension-card-extension-2')));
|
||||
final appMarkRect = tester.getRect(find.byKey(const ValueKey('primary-rail-app-mark')));
|
||||
expect(searchRect.height, closeTo(sortRect.height, 0.01));
|
||||
expect(searchRect.width, 240);
|
||||
expect(searchRect.width, lessThan(cardRect.width));
|
||||
expect(sortRect.left - searchRect.right, closeTo(10, 0.01));
|
||||
@@ -501,6 +502,7 @@ void main() {
|
||||
expect(find.byType(PrimaryRail), findsOneWidget);
|
||||
final compactSearchRect = tester.getRect(find.byKey(const ValueKey('extension-search-field-container')));
|
||||
expect(compactSearchRect.width, lessThan(240));
|
||||
expect(compactSearchRect.height, closeTo(sortRect.height, 0.01));
|
||||
expect(
|
||||
tester.getRect(find.byKey(const ValueKey('extension-sort-control'))).center.dy,
|
||||
closeTo(compactSearchRect.center.dy, 0.01),
|
||||
@@ -745,7 +747,7 @@ void main() {
|
||||
child: shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: const ExtensionsPage(),
|
||||
home: const AppComponentThemes(child: ExtensionsPage()),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1895,6 +1897,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();
|
||||
@@ -2337,7 +2373,9 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
final createDirectoryInput = find.byKey(const ValueKey('create-task-directory-input'));
|
||||
final createRenameInput = find.byKey(const ValueKey('create-task-rename-input'));
|
||||
final createConnectionsInput = find.byKey(const ValueKey('create-task-connections-input'));
|
||||
expect(tester.getSize(createDirectoryInput).height, tester.getSize(createRenameInput).height);
|
||||
expect(tester.getSize(createConnectionsInput), tester.getSize(createRenameInput));
|
||||
final createDirectoryField = tester.widget<shad.TextField>(createDirectoryInput);
|
||||
final createRenameField = tester.widget<shad.TextField>(
|
||||
find.descendant(of: createRenameInput, matching: find.byType(AppTextField)),
|
||||
@@ -2439,6 +2477,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()));
|
||||
@@ -2626,6 +2685,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,
|
||||
@@ -4854,7 +4914,7 @@ class _CreateTaskPageHarness extends StatelessWidget {
|
||||
return shad.ShadcnApp(
|
||||
theme: AppTheme.light(),
|
||||
materialTheme: AppTheme.materialLight(),
|
||||
home: const CreateTaskWindowPage(),
|
||||
home: const AppComponentThemes(child: CreateTaskWindowPage()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user