runJuiceTest<TBloc extends JuiceBloc<TState> , TState extends BlocState> function
Future<List<JuiceEmission<TState> > >
runJuiceTest<TBloc extends JuiceBloc<TState> , TState extends BlocState>({})
The body of juiceTest, callable directly (e.g. inside testWidgets, or
a test with custom setup). Returns the recorded emissions.
Implementation
Future<List<JuiceEmission<TState>>>
runJuiceTest<TBloc extends JuiceBloc<TState>, TState extends BlocState>({
required TBloc Function() build,
TState Function()? seed,
FutureOr<void> Function(TBloc bloc)? act,
Duration? wait,
int skip = 0,
Object Function()? expect,
Object Function()? errors,
FutureOr<void> Function(TBloc bloc)? verify,
bool allowInFlightAtClose = false,
}) async {
final previousLogger = JuiceLoggerConfig.logger;
final recorder = _RecordingLogger(previousLogger);
JuiceLoggerConfig.configureLogger(recorder);
final emissions = <JuiceEmission<TState>>[];
void Function()? untap;
var inFlightAtClose = 0;
late final TBloc bloc;
try {
bloc = build();
if (seed != null) {
await bloc.send(UpdateEvent<TState>(newState: seed()));
}
// Recording starts after the seed.
recorder.useCaseErrors.clear();
untap = bloc.tapEmissions((status) {
emissions.add(JuiceEmission<TState>(
status,
Set<String>.of(status.event?.groupsToRebuild ?? const <String>{}),
));
});
if (act != null) await act(bloc);
if (wait != null) await Future<void>.delayed(wait);
} finally {
untap?.call();
inFlightAtClose = recorder.openSpans.length;
// Close BEFORE asserting: nothing emitted from here on can leak into
// another test; in-flight work and late emissions are reported below.
try {
await bloc.close();
} finally {
JuiceLoggerConfig.configureLogger(previousLogger);
}
}
final recorded = emissions.skip(skip).toList();
if (expect != null) {
final expected = expect();
m.expect(recorded, _asEmissionsMatcher(expected),
reason: 'juiceTest: emitted $recorded');
}
if (errors != null) {
m.expect(recorder.useCaseErrors, m.wrapMatcher(errors()),
reason: 'juiceTest: use-case errors');
} else if (recorder.useCaseErrors.isNotEmpty) {
m.fail('juiceTest: unexpected use-case error(s): '
'${recorder.useCaseErrors}. Pass `errors:` to expect them.');
}
if (!allowInFlightAtClose &&
(inFlightAtClose > 0 || recorder.emissionsAfterClose > 0)) {
m.fail('juiceTest: $inFlightAtClose use case(s) still running when the '
'bloc closed (${recorder.emissionsAfterClose} emission(s) dropped '
'after close). Return the send() future from act, add `wait:`, or '
'pass `allowInFlightAtClose: true`.');
}
if (verify != null) await verify(bloc);
return emissions;
}