Ocideck/test/slide_list_panel_test.dart

104 lines
3.5 KiB
Dart
Raw Permalink Normal View History

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ocideck/models/slide.dart';
import 'package:ocideck/state/deck_provider.dart';
import 'package:ocideck/state/editor_provider.dart';
import 'package:ocideck/theme/app_theme.dart';
import 'package:ocideck/widgets/panels/slide_list_panel.dart';
import 'package:ocideck/widgets/slides/slide_thumbnail.dart';
void main() {
testWidgets('resizing the rail brings the edited slide back into view', (
tester,
) async {
final container = ProviderContainer();
addTearDown(container.dispose);
final deckNotifier = container.read(deckProvider.notifier);
deckNotifier.newDeck('Test');
for (var i = 0; i < 19; i++) {
deckNotifier.addSlide(SlideType.bullets);
}
container.read(editorProvider.notifier).select(12);
final width = ValueNotifier<double>(320);
addTearDown(width.dispose);
await tester.pumpWidget(
UncontrolledProviderScope(
container: container,
child: MaterialApp(
theme: AppTheme.light,
home: Scaffold(
body: Align(
alignment: Alignment.topLeft,
child: ValueListenableBuilder<double>(
valueListenable: width,
builder: (_, w, _) => SizedBox(
width: w,
height: 600,
child: SlideListPanel(railWidth: w),
),
),
),
),
),
),
);
await tester.pump();
// The selected slide (12) sits far below the fold and nothing scrolls it
// into view on its own.
bool slide12Visible() => find
.byWidgetPredicate((w) => w is SlideThumbnail && w.index == 12)
.evaluate()
.isNotEmpty;
expect(slide12Visible(), isFalse);
// Drag the rail wider: thumbnails change height, and once the resize
// settles the list scrolls the slide being edited back to the top.
width.value = 240;
await tester.pump();
await tester.pump(const Duration(milliseconds: 250)); // debounce fires
await tester.pump(); // coarse jump near the unbuilt slide
await tester.pump(); // precise reveal starts
await tester.pump(const Duration(milliseconds: 200)); // animateTo settles
expect(slide12Visible(), isTrue);
final rect = tester.getRect(
find.byWidgetPredicate((w) => w is SlideThumbnail && w.index == 12),
);
// At the top of the list area (below the panel header).
expect(rect.top, lessThan(120));
});
testWidgets('thumbnails expose one concise semantic label per slide', (
tester,
) async {
final handle = tester.ensureSemantics();
final container = ProviderContainer();
addTearDown(container.dispose);
final deckNotifier = container.read(deckProvider.notifier);
deckNotifier.newDeck('Test');
deckNotifier.addSlide(SlideType.bullets);
await tester.pumpWidget(
UncontrolledProviderScope(
container: container,
child: MaterialApp(
theme: AppTheme.light,
home: const Scaffold(
body: SizedBox(width: 320, child: SlideListPanel(railWidth: 320)),
),
),
),
);
await tester.pump();
// Screen readers get "Slide n/m: title-or-type" per card, instead of the
// full content of every mini preview.
expect(find.bySemanticsLabel(RegExp(r'^Slide 1/2: ')), findsOneWidget);
expect(find.bySemanticsLabel(RegExp(r'^Slide 2/2: ')), findsOneWidget);
handle.dispose();
});
}