scenario function
Implementation
@isTest
void scenario(
String description,
Future<void> Function(ScenarioTester s) body, {
Shots? shots,
Settle? settle,
bool? skip,
Timeout? timeout,
Object? tags,
ScenarioNetwork? network,
ScenarioReelEdit? reel,
}) {
// Captured as the scenario is *declared*, not read when it runs: a matrix
// declares this same body once per assignment, and each declaration keeps
// the one it was made under.
var assignment = scenarioAmbientAssignment;
// The folder's policy, where a `runScenarios(shots: ...)` set one — read
// here for the same reason, and beaten by anything this scenario said for
// itself. Nobody having spoken is `auto`, which is what it always was.
var policy = shots ?? scenarioAmbientShots ?? Shots.auto;
// The folder's settle policy, read here for the reason the shots policy
// is. Nobody having spoken is the bounded default.
var settling = settle ?? scenarioAmbientSettle ?? Settle.standard;
// How this scenario is cut as a reel, captured here for the reason the
// settle policy is. Null is "nobody wrote one", which a reel render reads
// as the stock edit — the plain film never reads it at all.
var edit = reel ?? scenarioAmbientReel;
// The folder's keyboard policy, captured as this scenario is declared for
// the reason the shots policy is: a matrix declares one body once per
// assignment, and each declaration keeps what it was made under. On unless
// the folder said otherwise — see [scenarioAmbientKeyboard] for what off
// restores.
var keyboard = scenarioAmbientKeyboard ?? true;
// The folder's shadow policy, captured here for the reason the keyboard is.
// On unless the folder said otherwise — see [runScenarios] for what
// `flutter_test`'s own default paints instead, and what it buys.
var shadows = scenarioAmbientShadows ?? true;
// The folder's network policy, captured here for the reason the two above
// are — and *only* the folder's. The rest of the ladder is resolved when the
// body runs, in [_reachOf]: under the runner, `scenarioRunArgs` is armed per
// scenario inside the walk, long after this function has run for every file,
// so a `--network=` read here would always be null and the flag would be a
// silent no-op. Two altitudes, two times, and each read where its answer
// exists.
var folderReach = scenarioAmbientNetwork;
var name =
scenarioAmbientIsMatrix && assignment != null && !assignment.isEmpty
? '$description [${assignment.label}]'
: description;
scenarioDeclarationSink?.add(name);
// Only the standalone lane needs it, and only it pays for it: under the
// runner the harness knows the file it generated the group from, and a suite
// that configures no destination captures nothing to file at all.
var source = ScenarioTester._screenshotsDestination == null
? null
: scenarioDeclaringFile(StackTrace.current);
// What the once-per-scenario network notices are remembered under. Built
// here because both halves only exist here: the ambient file is armed around
// this declaration and cleared before any body runs, and a matrix declares
// one body once per point — all of them under this one description, which is
// what makes a matrix say it once rather than once per axis.
var noticeKey = '${source ?? scenarioAmbientFile ?? ''}\u0000$description';
testWidgets(
name,
skip: skip,
// Under the harness the declared timeout is a progress deadline the
// harness keeps itself, and `test_api`'s own timer — which knows nothing
// about progress — is switched off so it cannot fire first. A bare
// `flutter test` has no harness, and keeps the timeout as written.
timeout: scenarioDefaultTimeout == null ? timeout : Timeout.none,
tags: tags,
(tester) async {
if (scenarioDefaultTimeout case var fallback?) {
scenarioDeclaredTimeout = timeout ?? fallback;
}
// Always pinned to something, and to [pinnedClockOrigin] unless somebody
// said otherwise — a run whose date is "whenever it happened" cannot be
// compared with the next one, which is what every surface reading these
// captures is for. `--clock now` and `FW_CLOCK=now` are how a run asks
// for the wall clock back; both resolve to an instant before they get
// here, so what ran is always a date somebody could write down.
//
// Except on the real clock, where nothing is compared and the backend
// answers with today's dates: there the wall clock is the clock, and a
// pin applies only when the run itself asked for one.
Future<void> scenarioBody(_PinnedClock? pinned) => _runScenario(
tester,
description,
body,
policy,
settling,
assignment,
source,
keyboard,
shadows,
_reachOf(network, folderReach, description, noticeKey),
statedNetwork: network != null,
noticeKey: noticeKey,
edit: edit,
pinned: pinned,
);
var origin = resolvedScenarioClockOrigin;
if (origin == null) return scenarioBody(null);
var pinned = _PinnedClock(origin, tester.binding.clock);
return withClock(Clock(pinned.now), () => scenarioBody(pinned));
},
);
}