scenario function

  1. @isTest
void scenario(
  1. String description,
  2. Future<void> body(
    1. ScenarioTester s
    ), {
  3. Shots? shots,
  4. Settle? settle,
  5. bool? skip,
  6. Timeout? timeout,
  7. Object? tags,
  8. ScenarioNetwork? network,
  9. ScenarioReelEdit? reel,
})

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));
    },
  );
}