Stage 2: Library search, filters and Exercise detail with Flip Animation
- LibraryFilter: word search on names; muscle (primary), equipment and level choices that widen within a filter and narrow across filters - ExerciseBrowser (reusable for the Plan picker) with search field, filter sheets showing live result counts, a clear button and an empty state - Exercise detail: start/end Flip Animation at each Mode's pace (still with reduced motion), facts, main/secondary muscles, numbered steps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1289fadb98
commit
786dd52e30
16 files changed
+1272
-32
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:zenxercise/src/exercises/views/exercise_detail_view.dart';
|
||||
import 'package:zenxercise/src/exercises/views/flip_animation.dart';
|
||||
|
||||
import '../support/test_app.dart';
|
||||
|
||||
void main() {
|
||||
Future<void> openLibrary(WidgetTester tester) async {
|
||||
usePhoneScreen(tester);
|
||||
await pumpApp(tester);
|
||||
await tester.tap(find.byTooltip('Library'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('search narrows the list and the count follows', (tester) async {
|
||||
await openLibrary(tester);
|
||||
|
||||
await tester.enterText(find.byType(TextField), 'calf');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Calf Stretch'), findsOneWidget);
|
||||
expect(find.text('90/90 Hamstring'), findsNothing);
|
||||
expect(find.text('1 of 2 stretches'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a muscle filter narrows the list and clearing brings everything back', (tester) async {
|
||||
await openLibrary(tester);
|
||||
|
||||
await tester.tap(find.text('Muscle'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.widgetWithText(FilterChip, 'Calves'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Show 1 result'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Calf Stretch'), findsOneWidget);
|
||||
expect(find.text('90/90 Hamstring'), findsNothing);
|
||||
|
||||
await tester.tap(find.byTooltip('Clear filters'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('90/90 Hamstring'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('no matches says so and offers to clear', (tester) async {
|
||||
await openLibrary(tester);
|
||||
|
||||
await tester.enterText(find.byType(TextField), 'zzz');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('No matches'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Clear filters'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('90/90 Hamstring'), findsOneWidget);
|
||||
expect(find.text('zzz'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('an Exercise opens its detail page, whose images flip', (tester) async {
|
||||
await openLibrary(tester);
|
||||
|
||||
await tester.tap(find.text('90/90 Hamstring'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ExerciseDetailView), findsOneWidget);
|
||||
int frame() => tester.state<FlipAnimationState>(find.byType(FlipAnimation)).frame;
|
||||
expect(frame(), 0);
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
expect(frame(), 1);
|
||||
|
||||
final page = find.descendant(of: find.byType(ExerciseDetailView), matching: find.byType(Scrollable)).first;
|
||||
for (final text in ['Hamstrings', 'Calves', 'Lie on your back.']) {
|
||||
await tester.scrollUntilVisible(find.text(text), 100, scrollable: page);
|
||||
expect(find.text(text), findsOneWidget);
|
||||
}
|
||||
|
||||
// Leave the page so its flip timer stops.
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user