Skip to content

Commit 445b0bf

Browse files
committed
test: align brief/station/share tests with current implementations
Eight failing tests were stale against deliberate code changes, not behavioural regressions. - exercise_share_format_test: bump golden to the shortened legend "øve | eval | rull / retur" that l10n now produces. - station_screen_test: tap-row navigation now opens a role sheet via ContextSheet.of(context).replace(RoleSheetTarget(...)) instead of pushing a GoRouter route. Add _StationSheetHarness that hosts StationExerciseScreen inside an open ContextSheet with a body builder that maps RoleSheetTarget to a stub Text widget. - brief_screen_test: - _tapAudience: SegmentedButton<BriefAudience> is now a slim PopupMenuButton<BriefAudience>; open the popup, then tap the matching menu item. - layout tests: audience picker lives in the AppBar at any width; verify presence + AppBar ancestry, drop the narrow-body assertion. - search: single-match queries wrap the hit in <curr-mark>, not <mark>; accept either tag. - in-doc TOC: only emitted in multi-exercise (program) mode, so switch the narrow-layout TOC assert to use programUuid. Files: - test/utils/exercise_share_format_test.dart - test/views/station_screen_test.dart - test/views/brief_screen_test.dart
1 parent 6f37abf commit 445b0bf

3 files changed

Lines changed: 101 additions & 27 deletions

File tree

test/utils/exercise_share_format_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void main() {
2727
'3. Stasjon C\n'
2828
'\n'
2929
'Generelt hver runde: 15 | 10 | 5 '
30-
'(øve | evaluere | rullere / inntransport)\n'
30+
'(øve | eval | rull / retur)\n'
3131
'\n'
3232
'Rullering (klokkeslett)\n'
3333
'Runde 1: 0930 | 0945 | 0955 (neste)\n'

test/views/brief_screen_test.dart

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,11 @@ String _markdownData(WidgetTester tester) {
150150
return tester.widget<MarkdownWidget>(find.byType(MarkdownWidget)).data;
151151
}
152152

153-
/// Tap the segmented button segment matching [label] and pump.
153+
/// Open the audience PopupMenuButton and tap the menu item with [label].
154154
Future<void> _tapAudience(WidgetTester tester, String label) async {
155-
final btn = find.descendant(
156-
of: find.byType(SegmentedButton<BriefAudience>),
157-
matching: find.text(label),
158-
);
159-
await tester.tap(btn);
155+
await tester.tap(find.byType(PopupMenuButton<BriefAudience>));
156+
await tester.pumpAndSettle();
157+
await tester.tap(find.text(label).last);
160158
await _awaitRender(tester);
161159
}
162160

@@ -257,7 +255,7 @@ void main() {
257255
});
258256

259257
group('BriefScreen — layout', () {
260-
testWidgets('narrow layout puts audience toggle in the body', (
258+
testWidgets('narrow layout keeps audience picker in the app bar', (
261259
tester,
262260
) async {
263261
tester.view.physicalSize = const Size(400, 800);
@@ -267,19 +265,19 @@ void main() {
267265
await tester.pumpWidget(_buildScreen(exerciseUuid: _exerciseUuid));
268266
await _awaitRender(tester);
269267

270-
final segButton = find.byType(SegmentedButton<BriefAudience>);
271-
expect(segButton, findsOneWidget);
272-
273-
final appBar = find.byType(AppBar);
274-
expect(appBar, findsOneWidget);
268+
final picker = find.byType(PopupMenuButton<BriefAudience>);
269+
expect(picker, findsOneWidget);
275270

276-
final appBarBottom = tester.getBottomLeft(appBar).dy;
277-
final toggleTop = tester.getTopLeft(segButton).dy;
278-
expect(toggleTop, greaterThanOrEqualTo(appBarBottom));
271+
// Audience picker lives in the AppBar regardless of width — the slim
272+
// PopupMenuButton replaces the old SegmentedButton-in-body layout.
273+
expect(
274+
find.descendant(of: find.byType(AppBar), matching: picker),
275+
findsOneWidget,
276+
);
279277
});
280278

281279
testWidgets(
282-
'wide layout puts audience toggle in app bar and shows TOC sidebar',
280+
'wide layout shows audience picker in app bar and TOC sidebar',
283281
(tester) async {
284282
tester.view.physicalSize = const Size(1200, 800);
285283
tester.view.devicePixelRatio = 1.0;
@@ -288,15 +286,15 @@ void main() {
288286
await tester.pumpWidget(_buildScreen(exerciseUuid: _exerciseUuid));
289287
await _awaitRender(tester);
290288

291-
final segButton = find.byType(SegmentedButton<BriefAudience>);
292-
expect(segButton, findsOneWidget);
289+
final picker = find.byType(PopupMenuButton<BriefAudience>);
290+
expect(picker, findsOneWidget);
293291

294292
// TOC sidebar heading visible
295293
expect(find.text('Contents'), findsOneWidget);
296294

297-
// The segmented button must be inside the AppBar widget tree
295+
// The picker must be inside the AppBar widget tree
298296
expect(
299-
find.descendant(of: find.byType(AppBar), matching: segButton),
297+
find.descendant(of: find.byType(AppBar), matching: picker),
300298
findsOneWidget,
301299
);
302300
},
@@ -317,7 +315,7 @@ void main() {
317315
});
318316

319317
testWidgets(
320-
'typing a query wraps matches in <mark> tags in MarkdownWidget.data',
318+
'typing a query wraps matches in <mark>/<curr-mark> tags in MarkdownWidget.data',
321319
(tester) async {
322320
await tester.pumpWidget(_buildScreen(exerciseUuid: _exerciseUuid));
323321
await _awaitRender(tester);
@@ -328,7 +326,16 @@ void main() {
328326
await tester.enterText(find.byType(TextField), 'Anne');
329327
await tester.pump();
330328

331-
expect(_markdownData(tester), contains('<mark>Anne'));
329+
// BriefScreen wraps the active match in <curr-mark> and any remaining
330+
// matches in <mark>. With a single match the wrapping tag is always
331+
// <curr-mark>; accept either to keep the assertion stable across
332+
// fixtures with more or fewer matches.
333+
final md = _markdownData(tester);
334+
expect(
335+
md.contains('<mark>Anne') || md.contains('<curr-mark>Anne'),
336+
isTrue,
337+
reason: 'expected one of <mark>Anne / <curr-mark>Anne in: $md',
338+
);
332339
},
333340
);
334341
});
@@ -432,7 +439,11 @@ void main() {
432439
tester.view.devicePixelRatio = 1.0;
433440
addTearDown(tester.view.resetPhysicalSize);
434441

435-
await tester.pumpWidget(_buildScreen(exerciseUuid: _exerciseUuid));
442+
// The in-doc TOC only renders in multi-exercise (program) mode; the
443+
// single-exercise template suppresses the program-level header block
444+
// entirely. Use programUuid so isSingleExercise=false and the
445+
// {{#if_in_doc_toc}} block emits the "## Innholdsfortegnelse" section.
446+
await tester.pumpWidget(_buildScreen(programUuid: _programUuid));
436447
await _awaitRender(tester);
437448

438449
expect(

test/views/station_screen_test.dart

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:ringdrill/models/station.dart';
1111
import 'package:ringdrill/services/program_service.dart';
1212
import 'package:ringdrill/views/roleplay_form_screen.dart';
1313
import 'package:ringdrill/views/station_screen.dart';
14+
import 'package:ringdrill/views/widgets/context_sheet.dart';
1415
import 'package:shared_preferences/shared_preferences.dart';
1516

1617
// ---------------------------------------------------------------------------
@@ -186,15 +187,24 @@ void main() {
186187
expect(find.byType(RolePlayFormScreen), findsOneWidget);
187188
});
188189

189-
testWidgets('tapping row body navigates to role detail', (tester) async {
190-
await tester.pumpWidget(_buildScreen(stationIndex: 0));
190+
testWidgets('tapping row body opens role sheet via ContextSheet', (tester) async {
191+
// Tap path now calls ContextSheet.of(context).replace(RoleSheetTarget(...))
192+
// instead of pushing a GoRouter route, so the screen must be hosted inside
193+
// an open ContextSheet for the replace assertion to hold.
194+
await tester.pumpWidget(MaterialApp(
195+
localizationsDelegates: AppLocalizations.localizationsDelegates,
196+
supportedLocales: AppLocalizations.supportedLocales,
197+
home: const _StationSheetHarness(stationIndex: 0),
198+
));
199+
await tester.pump(); // post-frame callback fires → show()
200+
await tester.pump(); // showModalBottomSheet starts
191201
await tester.pumpAndSettle();
192202

193203
// The InkWell wrapping row content (title text is tappable)
194204
await tester.tap(find.text('Pasient A'));
195205
await tester.pumpAndSettle();
196206

197-
// Stub route renders the UUID
207+
// Stub body builder renders the UUID after replace(RoleSheetTarget(...)).
198208
expect(find.text('RolePlay ${_roleAtStation0.uuid}'), findsOneWidget);
199209
});
200210

@@ -288,3 +298,56 @@ void main() {
288298
}
289299
});
290300
}
301+
302+
/// Hosts [StationExerciseScreen] inside an open [ContextSheet] so role-row
303+
/// taps that call `ContextSheet.of(context).replace(RoleSheetTarget(...))`
304+
/// resolve and update the sheet body. The body builder maps RoleSheetTarget
305+
/// to a plain Text widget so the test can assert against the UUID.
306+
class _StationSheetHarness extends StatefulWidget {
307+
const _StationSheetHarness({required this.stationIndex});
308+
309+
final int stationIndex;
310+
311+
@override
312+
State<_StationSheetHarness> createState() => _StationSheetHarnessState();
313+
}
314+
315+
class _StationSheetHarnessState extends State<_StationSheetHarness> {
316+
final _controller = ContextSheetController();
317+
318+
@override
319+
void initState() {
320+
super.initState();
321+
WidgetsBinding.instance.addPostFrameCallback((_) {
322+
_controller.show(
323+
context,
324+
StationSheetTarget(
325+
exerciseUuid: _exerciseUuid,
326+
stationIndex: widget.stationIndex,
327+
),
328+
);
329+
});
330+
}
331+
332+
@override
333+
void dispose() {
334+
_controller.dispose();
335+
super.dispose();
336+
}
337+
338+
@override
339+
Widget build(BuildContext context) {
340+
return ContextSheet(
341+
controller: _controller,
342+
bodyBuilder: (ctx, target) => switch (target) {
343+
StationSheetTarget(:final exerciseUuid, :final stationIndex) =>
344+
StationExerciseScreen(uuid: exerciseUuid, stationIndex: stationIndex),
345+
RoleSheetTarget(:final rolePlayUuid) => Scaffold(
346+
body: Center(child: Text('RolePlay $rolePlayUuid')),
347+
),
348+
_ => const SizedBox.shrink(),
349+
},
350+
child: const Scaffold(body: SizedBox.shrink()),
351+
);
352+
}
353+
}

0 commit comments

Comments
 (0)