213 lines
6.7 KiB
Dart
213 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:tasq/theme/app_theme.dart';
|
|
import 'package:tasq/theme/app_surfaces.dart';
|
|
import 'package:tasq/widgets/tasq_adaptive_list.dart';
|
|
|
|
void main() {
|
|
testWidgets('AppTheme sets cardTheme elevation to 1 (M3 tonal)', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: Builder(
|
|
builder: (context) {
|
|
final elevation = Theme.of(context).cardTheme.elevation;
|
|
expect(elevation, isNotNull);
|
|
// M3 Expressive uses tonal elevation — minimal 0-1 shadow.
|
|
expect(elevation, inInclusiveRange(0.0, 2.0));
|
|
expect(elevation, equals(1));
|
|
return const SizedBox.shrink();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
testWidgets('Card without explicit elevation uses theme elevation', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: const Scaffold(
|
|
body: Card(child: SizedBox(width: 20, height: 20)),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Find the Material that actually paints the Card and assert elevation
|
|
final materialFinder = find.descendant(
|
|
of: find.byType(Card),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
expect(materialFinder, findsWidgets);
|
|
|
|
final material = tester.widget<Material>(materialFinder.first);
|
|
expect(material.elevation, isNotNull);
|
|
// M3 Expressive: tonal elevation is 0-2, not the M2 range of 2-4.
|
|
expect(material.elevation, inInclusiveRange(0.0, 2.0));
|
|
});
|
|
|
|
testWidgets(
|
|
'TasQAdaptiveList mobile Card inherits theme elevation and uses compact radius',
|
|
(WidgetTester tester) async {
|
|
final theme = AppTheme.light();
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: MediaQuery(
|
|
data: const MediaQueryData(size: Size(320, 800)),
|
|
child: Scaffold(
|
|
body: Center(
|
|
child: SizedBox(
|
|
width: 320,
|
|
child: TasQAdaptiveList<int>(
|
|
items: const [1],
|
|
columns: const [],
|
|
mobileTileBuilder: (context, item, actions) =>
|
|
const Card(child: SizedBox(width: 100, height: 40)),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// the Card returned by the mobile tile builder should be re-wrapped by
|
|
// _MobileTile; assert that the visible Card uses theme elevation and a
|
|
// compact 12px radius (per mobile rules).
|
|
final cardFinder = find.byType(Card);
|
|
expect(cardFinder, findsWidgets);
|
|
|
|
final Card cardWidget = tester.widget<Card>(cardFinder.first);
|
|
final shape = cardWidget.shape as RoundedRectangleBorder;
|
|
final radius = (shape.borderRadius as BorderRadius).topLeft.x;
|
|
|
|
expect(
|
|
radius,
|
|
equals(
|
|
AppSurfaces.of(tester.element(cardFinder.first)).compactCardRadius,
|
|
),
|
|
);
|
|
|
|
// Verify the painted Material has the theme elevation
|
|
final materialFinder = find.descendant(
|
|
of: find.byType(Card),
|
|
matching: find.byType(Material),
|
|
);
|
|
final material = tester.widget<Material>(materialFinder.first);
|
|
expect(material.elevation, equals(theme.cardTheme.elevation));
|
|
},
|
|
);
|
|
|
|
// new regression tests for desktop layout adjustments
|
|
testWidgets(
|
|
'Desktop adaptive list responsive width and horizontal scrollbar',
|
|
(tester) async {
|
|
final theme = AppTheme.light();
|
|
|
|
Future<double> contentWidthFor(double screenWidth) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: theme,
|
|
home: MediaQuery(
|
|
data: MediaQueryData(size: Size(screenWidth, 800)),
|
|
child: Scaffold(
|
|
body: TasQAdaptiveList<int>(
|
|
items: List.generate(100, (i) => i),
|
|
columns: List.generate(
|
|
20,
|
|
(i) => TasQColumn<int>(
|
|
header: 'C$i',
|
|
cellBuilder: (c, t) => Text('$t'),
|
|
),
|
|
),
|
|
mobileTileBuilder: (c, t, a) => const SizedBox.shrink(),
|
|
rowActions: (_) => [],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
final box = tester.widget<SizedBox>(
|
|
find.byKey(const Key('adaptive_list_content')),
|
|
);
|
|
return box.width!;
|
|
}
|
|
|
|
final narrow = await contentWidthFor(1000);
|
|
final wide = await contentWidthFor(2000);
|
|
|
|
expect(find.byType(Scrollbar), findsWidgets);
|
|
expect(narrow / 1000, greaterThan(wide / 2000));
|
|
},
|
|
);
|
|
|
|
testWidgets('Desktop pagination forwards onPageChanged callback', (
|
|
WidgetTester tester,
|
|
) async {
|
|
int? receivedIndex;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: MediaQuery(
|
|
data: const MediaQueryData(size: Size(1200, 800)),
|
|
child: Scaffold(
|
|
body: TasQAdaptiveList<int>(
|
|
items: List.generate(60, (i) => i),
|
|
columns: [
|
|
TasQColumn<int>(header: 'C', cellBuilder: (c, t) => Text('$t')),
|
|
],
|
|
mobileTileBuilder: (c, t, a) => const SizedBox.shrink(),
|
|
rowsPerPage: 10,
|
|
onPageChanged: (firstRow) {
|
|
receivedIndex = firstRow;
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// locate the PaginatedDataTable and manually invoke the callback to
|
|
// simulate the user requesting the second page
|
|
final table = tester.widget<PaginatedDataTable>(
|
|
find.byType(PaginatedDataTable),
|
|
);
|
|
expect(table.onPageChanged, isNotNull);
|
|
|
|
table.onPageChanged!(10);
|
|
expect(receivedIndex, equals(10));
|
|
});
|
|
|
|
testWidgets('AppSurfaces tokens are present and dialog/card radii differ', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: AppTheme.light(),
|
|
home: Builder(
|
|
builder: (context) {
|
|
final surfaces = AppSurfaces.of(context);
|
|
expect(surfaces.compactCardRadius, lessThan(surfaces.cardRadius));
|
|
expect(surfaces.dialogRadius, greaterThanOrEqualTo(18));
|
|
final dialogShape =
|
|
(surfaces.dialogShape.borderRadius as BorderRadius).topLeft.x;
|
|
final compactShape =
|
|
(surfaces.compactShape.borderRadius as BorderRadius).topLeft.x;
|
|
expect(dialogShape, greaterThan(compactShape));
|
|
return const SizedBox.shrink();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
});
|
|
}
|