196 lines
5.7 KiB
Dart
196 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:krow_worker_app/core/theme.dart';
|
|
import 'package:krow_worker_app/widgets/common.dart';
|
|
import 'package:krow_worker_app/widgets/glass_segments.dart';
|
|
import 'package:krow_worker_app/widgets/krow_nav_bar.dart';
|
|
|
|
Widget wrap(Widget child) => MaterialApp(
|
|
theme: buildKrowTheme(),
|
|
home: Scaffold(body: child),
|
|
);
|
|
|
|
void main() {
|
|
group('KrowNavBar', () {
|
|
const items = [
|
|
KrowNavItem(icon: Icons.home, label: 'Today'),
|
|
KrowNavItem(icon: Icons.work, label: 'Shifts'),
|
|
KrowNavItem(icon: Icons.timer, label: 'Clock', showDot: true),
|
|
];
|
|
|
|
testWidgets('renders every label', (tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(KrowNavBar(items: items, currentIndex: 0, onSelect: (_) {})),
|
|
);
|
|
|
|
expect(find.text('Today'), findsOneWidget);
|
|
expect(find.text('Shifts'), findsOneWidget);
|
|
expect(find.text('Clock'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('reports the tapped index', (tester) async {
|
|
var selected = -1;
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
KrowNavBar(
|
|
items: items,
|
|
currentIndex: 0,
|
|
onSelect: (i) => selected = i,
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Clock'));
|
|
expect(selected, 2);
|
|
});
|
|
|
|
testWidgets('announces a tab that needs attention', (tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(KrowNavBar(items: items, currentIndex: 0, onSelect: (_) {})),
|
|
);
|
|
|
|
// The dot is not readable, so the state has to reach the semantics tree.
|
|
expect(find.bySemanticsLabel('Clock, needs attention'), findsOneWidget);
|
|
expect(find.bySemanticsLabel('Today'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('the indicator travels rather than jumping', (tester) async {
|
|
var index = 0;
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
StatefulBuilder(
|
|
builder: (context, setState) => KrowNavBar(
|
|
items: items,
|
|
currentIndex: index,
|
|
onSelect: (i) => setState(() => index = i),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
Rect pillRect() => tester.getRect(find.byKey(KrowNavBar.indicatorKey));
|
|
|
|
final start = pillRect().center.dx;
|
|
await tester.tap(find.text('Clock'));
|
|
await tester.pump();
|
|
await tester.pump(const Duration(milliseconds: 120));
|
|
|
|
// Part way through it should be between the two tabs, not already there.
|
|
final middle = pillRect().center.dx;
|
|
expect(middle, greaterThan(start));
|
|
|
|
await tester.pumpAndSettle();
|
|
final end = pillRect().center.dx;
|
|
expect(end, greaterThan(middle));
|
|
});
|
|
});
|
|
|
|
group('StatSlabRow', () {
|
|
// Every real use sits in a scrolling column, where the height is
|
|
// unbounded. Testing it against a bounded Scaffold body hides exactly the
|
|
// layout errors this row is prone to.
|
|
Widget inScrollable(Widget child) =>
|
|
wrap(ListView(children: [const SizedBox(height: 8), child]));
|
|
|
|
testWidgets('lays out inside a scrollable', (tester) async {
|
|
await tester.pumpWidget(
|
|
inScrollable(
|
|
const StatSlabRow(
|
|
slabs: [
|
|
StatSlab('Clock in', '6:01 PM'),
|
|
StatSlab('Clock out', '11:50 PM'),
|
|
StatSlab('Hours', '5h 19m'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(tester.takeException(), isNull);
|
|
// A row that tried to fill unbounded height would be enormous.
|
|
expect(tester.getSize(find.byType(StatSlabRow)).height, lessThan(120));
|
|
});
|
|
|
|
testWidgets('keeps every card the same height, however long the text', (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
inScrollable(
|
|
const StatSlabRow(
|
|
slabs: [
|
|
// The middle one is what used to wrap on both lines and push
|
|
// its card taller than the two beside it.
|
|
StatSlab('Clock in', '6:01 PM'),
|
|
StatSlab('Clock out', '11:50 PM'),
|
|
StatSlab('Hours', '5h 19m'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
final heights = tester
|
|
.widgetList<StatSlab>(find.byType(StatSlab))
|
|
.map((slab) => tester.getSize(find.byWidget(slab)).height)
|
|
.toSet();
|
|
|
|
expect(heights.length, 1, reason: 'cards should be one height');
|
|
});
|
|
|
|
testWidgets('scales long values down instead of wrapping', (tester) async {
|
|
await tester.pumpWidget(
|
|
inScrollable(
|
|
const StatSlabRow(
|
|
slabs: [
|
|
StatSlab('Paid', r'$1,234.56'),
|
|
StatSlab('Rate', r'$21.00/hr'),
|
|
StatSlab('Hours', '5h 19m'),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
for (final text in tester.widgetList<Text>(find.byType(Text))) {
|
|
expect(text.maxLines, 1);
|
|
}
|
|
});
|
|
});
|
|
|
|
group('GlassSegments', () {
|
|
testWidgets('reports the tapped segment', (tester) async {
|
|
String? picked;
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
GlassSegments<String>(
|
|
segments: const [('a', 'Week'), ('b', 'Month'), ('c', 'Year')],
|
|
value: 'a',
|
|
onChanged: (v) => picked = v,
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Year'));
|
|
expect(picked, 'c');
|
|
});
|
|
|
|
testWidgets('ignores a tap on the segment already selected', (
|
|
tester,
|
|
) async {
|
|
var calls = 0;
|
|
await tester.pumpWidget(
|
|
wrap(
|
|
GlassSegments<String>(
|
|
segments: const [('a', 'Week'), ('b', 'Month')],
|
|
value: 'a',
|
|
onChanged: (_) => calls++,
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.tap(find.text('Week'));
|
|
expect(calls, 0);
|
|
|
|
await tester.tap(find.text('Month'));
|
|
expect(calls, 1);
|
|
});
|
|
});
|
|
}
|