Initial commit: Krow Worker App

This commit is contained in:
R-Bharathraj
2026-09-17 22:26:11 +05:30
commit cafffd27e0
113 changed files with 13067 additions and 0 deletions

195
test/widgets_test.dart Normal file
View File

@@ -0,0 +1,195 @@
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);
});
});
}