Finished updating the tour

This commit is contained in:
Courtney Arnold 2026-08-15 20:07:16 -05:00
parent 606106e5ec
commit 2fcc6dcab7
7 changed files with 288 additions and 83 deletions

View file

@ -129,11 +129,17 @@ void main() {
return appState;
}
testWidgets('shows step 1 (Receipts) automatically on first launch', (tester) async {
testWidgets('shows the "What This App Does" intro automatically on first launch, with the '
'refund-rate and lawmakers disclaimer', (tester) async {
await pumpShellWithFreshTour(tester);
expect(find.text('Add Receipts Here'), findsOneWidget);
expect(find.text('Step 1 of 4'), findsOneWidget);
expect(find.text('What This App Does'), findsOneWidget);
expect(find.text('Step 1 of 6'), findsOneWidget);
expect(find.textContaining('12.5¢'), findsOneWidget);
expect(find.textContaining('Disclaimer'), findsOneWidget);
expect(find.textContaining('Missouri lawmakers'), findsOneWidget);
// No specific element to spotlight for the intro — stays on
// whichever tab MainShell defaults to.
expect(find.widgetWithText(AppBar, 'Receipts'), findsOneWidget);
});
@ -150,13 +156,19 @@ void main() {
);
await tester.pumpAndSettle();
expect(find.text('Add Receipts Here'), findsNothing);
expect(find.text('What This App Does'), findsNothing);
});
testWidgets('tapping the dimmed barrier does not dismiss the tour or reach the highlighted button',
(tester) async {
await pumpShellWithFreshTour(tester);
// Advance to the Receipts step, which is the one with an actual
// highlighted, tappable element (the "+") behind the barrier.
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Add Receipts Here'), findsOneWidget);
// Top-left corner, away from the card and from the highlighted "+"
// (which sits top-right in the AppBar).
await tester.tapAt(const Offset(10, 10));
@ -168,28 +180,44 @@ void main() {
"vehicles yet — was not actually reached");
});
testWidgets('Next advances through all four steps, switching tabs and pushing Settings > Data',
testWidgets(
'Next advances through all six steps, switching tabs, then pushing Settings > Data',
(tester) async {
await pumpShellWithFreshTour(tester);
expect(find.text('What This App Does'), findsOneWidget);
expect(find.widgetWithText(AppBar, 'Receipts'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Add Receipts Here'), findsOneWidget);
expect(find.text('Step 2 of 6'), findsOneWidget);
expect(find.widgetWithText(AppBar, 'Receipts'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Add Vehicles Here'), findsOneWidget);
expect(find.text('Step 2 of 4'), findsOneWidget);
expect(find.text('Step 3 of 6'), findsOneWidget);
expect(find.widgetWithText(AppBar, 'Vehicles'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Choose a Report Date Range'), findsOneWidget);
expect(find.text('Step 4 of 6'), findsOneWidget);
expect(find.widgetWithText(AppBar, 'Fuel Report'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Find Data Settings'), findsOneWidget);
expect(find.text('Step 5 of 6'), findsOneWidget);
expect(find.widgetWithText(AppBar, 'Settings'), findsOneWidget);
expect(find.byType(DataSettingsScreen), findsNothing,
reason: 'still on the Settings landing page, not pushed into Data yet');
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
expect(find.text('Back Up to the Cloud'), findsOneWidget);
expect(find.text('Step 4 of 4'), findsOneWidget);
expect(find.text('Step 6 of 6'), findsOneWidget);
expect(find.byType(DataSettingsScreen), findsOneWidget, reason: 'tour pushed Settings > Data');
expect(find.text('Cloud Storage'), findsOneWidget);
expect(find.widgetWithText(FilledButton, 'Got It'), findsOneWidget,
@ -201,7 +229,7 @@ void main() {
(tester) async {
final appState = await pumpShellWithFreshTour(tester);
for (var i = 0; i < 3; i++) {
for (var i = 0; i < 5; i++) {
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
}
@ -226,7 +254,7 @@ void main() {
await tester.tap(find.widgetWithText(TextButton, 'Skip Tour'));
await tester.pumpAndSettle();
expect(find.text('Add Receipts Here'), findsNothing);
expect(find.text('What This App Does'), findsNothing);
expect(appState.hasSeenOnboardingTour, isTrue);
expect(find.widgetWithText(AppBar, 'Receipts'), findsOneWidget,
reason: 'stayed on the tab it started on');
@ -236,7 +264,7 @@ void main() {
(tester) async {
await pumpShellWithFreshTour(tester);
for (var i = 0; i < 3; i++) {
for (var i = 0; i < 5; i++) {
await tester.tap(find.widgetWithText(FilledButton, 'Next'));
await tester.pumpAndSettle();
}

View file

@ -14,7 +14,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'Example Title',
description: 'Example description text.',
stepNumber: 2,
@ -34,7 +34,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'T',
description: 'D',
stepNumber: 1,
@ -48,7 +48,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'T',
description: 'D',
stepNumber: 3,
@ -68,7 +68,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'T',
description: 'D',
stepNumber: 1,
@ -92,7 +92,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'T',
description: 'D',
stepNumber: 1,
@ -112,7 +112,7 @@ void main() {
await tester.pumpWidget(wrap(
OnboardingTourOverlay(
targetKey: targetKey,
targetKeys: [targetKey],
title: 'T',
description: 'D',
stepNumber: 1,
@ -129,4 +129,75 @@ void main() {
expect(nextTapped, isFalse);
expect(skipTapped, isFalse);
});
testWidgets('highlights every key in targetKeys, not just the first', (tester) async {
final topKey = GlobalKey();
final bottomKey = GlobalKey();
Widget buildTree({required bool showOverlay}) => MaterialApp(
home: Scaffold(
body: Stack(
children: [
Positioned(top: 0, left: 0, child: SizedBox(key: topKey, width: 40, height: 40)),
Positioned(
bottom: 0, left: 0, child: SizedBox(key: bottomKey, width: 40, height: 40)),
if (showOverlay)
OnboardingTourOverlay(
targetKeys: [topKey, bottomKey],
title: 'Both Highlighted',
description: 'D',
stepNumber: 1,
totalSteps: 1,
onNext: () {},
onSkip: () {},
),
],
),
),
);
// Two frames, matching how MainShell actually uses this: the target
// keys' RenderBoxes need to already exist and be laid out (from a
// prior frame) before the overlay's own build() can find their real
// position — querying them in the very same frame they're first
// mounted finds nothing yet (layout hasn't run for that frame at
// build time).
await tester.pumpWidget(buildTree(showOverlay: false));
await tester.pumpWidget(buildTree(showOverlay: true));
// One highlight border per resolved key.
expect(find.byType(DecoratedBox), findsNWidgets(2));
expect(find.text('Both Highlighted'), findsOneWidget);
});
testWidgets('still renders correctly when only some of targetKeys resolve', (tester) async {
final resolvedKey = GlobalKey();
final unresolvedKey = GlobalKey(); // never attached to anything
Widget buildTree({required bool showOverlay}) => MaterialApp(
home: Scaffold(
body: Stack(
children: [
SizedBox(key: resolvedKey, width: 40, height: 40),
if (showOverlay)
OnboardingTourOverlay(
targetKeys: [resolvedKey, unresolvedKey],
title: 'T',
description: 'D',
stepNumber: 1,
totalSteps: 1,
onNext: () {},
onSkip: () {},
),
],
),
),
);
await tester.pumpWidget(buildTree(showOverlay: false));
await tester.pumpWidget(buildTree(showOverlay: true));
expect(find.byType(DecoratedBox), findsNWidgets(1), reason: 'only the resolved key gets a border');
expect(find.text('T'), findsOneWidget);
});
}