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 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, 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 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'); }); }); }