232 lines
7.5 KiB
Dart
232 lines
7.5 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:kooltap/l10n/app_localizations.dart';
|
||
import 'package:kooltap/models/bar_tab.dart';
|
||
import 'package:kooltap/utils/name_index.dart';
|
||
import 'package:kooltap/views/bar/selected_tab_details.dart';
|
||
import 'package:kooltap/views/bar/tab_panel.dart';
|
||
|
||
const _names = [
|
||
'Zoë', 'anna', 'Émile', 'Bram', 'Mieke', 'Marc', 'Ward', 'Tom', 'Sofie', //
|
||
'Lotte', 'Koen', 'Jan', 'Inge', 'Hanne', 'Greet', 'Frank', 'Dirk', 'Chris',
|
||
'Nele', 'Olaf', 'Pieter', 'Rik', 'Vera', 'Yves', '2de tafel', 'Eva',
|
||
];
|
||
|
||
void main() {
|
||
group('name index', () {
|
||
test('folds case and accents into the index letter', () {
|
||
expect(indexLetterOf('émile'), 'E');
|
||
expect(indexLetterOf('Zoë'), 'Z');
|
||
expect(indexLetterOf(' anna'), 'A');
|
||
expect(indexLetterOf('2de tafel'), '#');
|
||
expect(indexLetterOf(''), '#');
|
||
});
|
||
|
||
test('sorts A–Z regardless of case and accents, # last', () {
|
||
final sorted = ['Zoë', 'émile', 'Eva', 'anna', '2de tafel', 'Bram']
|
||
..sort(compareByIndex);
|
||
|
||
expect(sorted, ['anna', 'Bram', 'émile', 'Eva', 'Zoë', '2de tafel']);
|
||
});
|
||
});
|
||
|
||
group('TabPanel letter index', () {
|
||
final tabs = [
|
||
for (final (index, name) in _names.indexed)
|
||
BarTab(
|
||
id: 'tab-$index',
|
||
customerName: name,
|
||
openedAt: DateTime(2026, 10, 11, 12, index),
|
||
items: const [],
|
||
),
|
||
];
|
||
|
||
Future<void> pumpPanel(
|
||
WidgetTester tester, {
|
||
bool showSearch = false,
|
||
BarTab? selectedTab,
|
||
}) async {
|
||
tester.view.physicalSize = const Size(500, 800);
|
||
tester.view.devicePixelRatio = 1;
|
||
addTearDown(tester.view.reset);
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||
supportedLocales: AppLocalizations.supportedLocales,
|
||
home: Scaffold(
|
||
body: TabPanel(
|
||
tabs: tabs,
|
||
selectedTab: selectedTab,
|
||
selectedTabId: selectedTab?.id,
|
||
stockByProductId: const {},
|
||
showSearch: showSearch,
|
||
onTabSelected: (_) {},
|
||
onItemQuantityChanged: (_, _) async {},
|
||
onCloseTabPressed: () {},
|
||
onTabEdited: (_) async {},
|
||
onTabDeleted: (_) async {},
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
String firstVisibleName(WidgetTester tester) {
|
||
final list = tester.getRect(find.byType(OpenTabsList));
|
||
final visible = _names.where((name) {
|
||
final finder = find.text(name);
|
||
if (finder.evaluate().isEmpty) return false;
|
||
final rect = tester.getRect(finder);
|
||
return rect.top >= list.top && rect.bottom <= list.bottom;
|
||
}).toList();
|
||
|
||
return visible.reduce(
|
||
(a, b) =>
|
||
tester.getRect(find.text(a)).top < tester.getRect(find.text(b)).top
|
||
? a
|
||
: b,
|
||
);
|
||
}
|
||
|
||
Future<void> tapLetter(WidgetTester tester, String letter) async {
|
||
await tester.tap(
|
||
find.descendant(
|
||
of: find.byWidgetPredicate(
|
||
(widget) => widget.runtimeType.toString() == '_LetterIndexBar',
|
||
),
|
||
matching: find.text(letter),
|
||
),
|
||
);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
testWidgets('lists the tabs alphabetically', (tester) async {
|
||
await pumpPanel(tester);
|
||
|
||
expect(firstVisibleName(tester), 'anna');
|
||
});
|
||
|
||
testWidgets('tapping a letter scrolls to its first name', (tester) async {
|
||
await pumpPanel(tester);
|
||
|
||
await tapLetter(tester, 'M');
|
||
expect(firstVisibleName(tester), 'Marc');
|
||
|
||
await tapLetter(tester, 'E');
|
||
expect(firstVisibleName(tester), 'Émile');
|
||
});
|
||
|
||
testWidgets('shows the search bar only when asked', (tester) async {
|
||
await pumpPanel(tester);
|
||
expect(find.byType(TextField), findsNothing);
|
||
|
||
await pumpPanel(tester, showSearch: true);
|
||
expect(find.byType(TextField), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('closing the search clears the filter', (tester) async {
|
||
await pumpPanel(tester, showSearch: true);
|
||
await tester.enterText(find.byType(TextField), 'Marc');
|
||
await tester.pump();
|
||
expect(find.text('anna'), findsNothing);
|
||
|
||
await pumpPanel(tester);
|
||
expect(find.text('anna'), findsOneWidget);
|
||
});
|
||
|
||
group('collapsing', () {
|
||
double namesHeight(WidgetTester tester) =>
|
||
tester.getSize(find.byType(OpenTabsList)).height;
|
||
|
||
testWidgets('starts with the names at their usual height', (
|
||
tester,
|
||
) async {
|
||
await pumpPanel(tester, selectedTab: tabs.first);
|
||
|
||
expect(namesHeight(tester), 246);
|
||
});
|
||
|
||
testWidgets('tapping a name collapses the names', (tester) async {
|
||
await pumpPanel(tester, selectedTab: tabs.first);
|
||
|
||
await tester.tap(find.text('Bram'));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(namesHeight(tester), lessThan(200));
|
||
});
|
||
|
||
testWidgets('touching the items collapses the names', (tester) async {
|
||
await pumpPanel(tester, selectedTab: tabs.first);
|
||
|
||
await tester.tap(find.byType(SelectedTabDetails), warnIfMissed: false);
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(namesHeight(tester), lessThan(200));
|
||
});
|
||
|
||
testWidgets('lines the selected name up on top of its items', (
|
||
tester,
|
||
) async {
|
||
final marc = tabs.firstWhere((tab) => tab.customerName == 'Marc');
|
||
await pumpPanel(tester, selectedTab: marc);
|
||
await tester.tap(find.byType(SelectedTabDetails), warnIfMissed: false);
|
||
await tester.pumpAndSettle();
|
||
|
||
final list = tester.getRect(find.byType(OpenTabsList));
|
||
final details = tester.getRect(find.byType(SelectedTabDetails));
|
||
final tile = tester.getRect(
|
||
find
|
||
.ancestor(of: find.text('Marc'), matching: find.byType(ClipRRect))
|
||
.first,
|
||
);
|
||
|
||
expect(tile.bottom, closeTo(list.bottom, 1));
|
||
expect(details.top, greaterThan(tile.bottom));
|
||
expect(
|
||
find.byWidgetPredicate(
|
||
(widget) => widget is Divider && widget.height == 28,
|
||
),
|
||
findsNothing,
|
||
);
|
||
});
|
||
|
||
testWidgets('browsing the names shows the divider again', (tester) async {
|
||
final marc = tabs.firstWhere((tab) => tab.customerName == 'Marc');
|
||
await pumpPanel(tester, selectedTab: marc);
|
||
await tester.tap(find.byType(SelectedTabDetails), warnIfMissed: false);
|
||
await tester.pumpAndSettle();
|
||
|
||
await tester.drag(find.byType(OpenTabsList), const Offset(0, -40));
|
||
await tester.pumpAndSettle();
|
||
|
||
expect(
|
||
find.byWidgetPredicate(
|
||
(widget) => widget is Divider && widget.height == 28,
|
||
),
|
||
findsOneWidget,
|
||
);
|
||
});
|
||
|
||
testWidgets('scrolling the names collapses the items', (tester) async {
|
||
await pumpPanel(tester, selectedTab: tabs.first);
|
||
await tester.tap(find.byType(SelectedTabDetails), warnIfMissed: false);
|
||
await tester.pumpAndSettle();
|
||
|
||
await tester.drag(find.byType(OpenTabsList), const Offset(0, -40));
|
||
await tester.pumpAndSettle();
|
||
|
||
final panel = tester.getSize(find.byType(TabPanel)).height;
|
||
expect(namesHeight(tester), greaterThan(panel * 0.75));
|
||
});
|
||
});
|
||
|
||
testWidgets('a letter without names goes to the next one', (tester) async {
|
||
await pumpPanel(tester);
|
||
|
||
await tapLetter(tester, 'Q');
|
||
expect(firstVisibleName(tester), 'Rik');
|
||
});
|
||
});
|
||
}
|