runWith method

Future<PlaybookReport> runWith(
  1. PlaybookSession session, {
  2. PlaybookSpeed? speed,
  3. Map<String, dynamic>? data,
  4. void emit(
    1. Map<String, Object?> event
    )?,
  5. List<String> path = const [],
  6. int depth = 0,
  7. String caption = '',
})

Runs this playbook inside session, which open started.

play() and Metro's runner use this; most code calls run. path lists the playbooks this one runs inside, caption is the caption prefix of the step that plays it, and emit receives the run's events.

Implementation

Future<PlaybookReport> runWith(
  PlaybookSession session, {
  PlaybookSpeed? speed,
  Map<String, dynamic>? data,
  void Function(Map<String, Object?> event)? emit,
  List<String> path = const [],
  int depth = 0,
  String caption = '',
}) async {
  final String name = PlaybookRegistry.nameOf(this);
  if (path.contains(name)) {
    throw PlaybookFailure('Playbook cycle: ${[...path, name].join(' → ')}');
  }
  if (path.length >= maxDepth) {
    throw PlaybookFailure(
      'Playbooks nest at most $maxDepth deep: ${[...path, name].join(' → ')}',
    );
  }

  final PlaybookSpeed pace = speed ?? this.speed;
  final Map<String, dynamic> merged = {...defaults, ...?data};
  final PlaybookActor actor = PlaybookActor(
    session: session,
    speed: pace,
    data: merged,
  );
  final List<String> chain = [...path, name];
  final PlaybookStepContext context = PlaybookStepContext(
    playbook: name,
    path: chain,
    depth: depth,
    caption: caption,
    emit: emit,
  );
  final PlaybookClock clock = session.clock;
  final Duration start = clock.elapsed;
  final List<PlaybookStepResult> results = [];
  List<PlaybookStep> list = const [];
  PlaybookStepResult? setUpFailure;

  final ValueNotifier<bool>? finger = session.overlay?.showFinger;
  final bool? fingerBefore = finger?.value;
  finger?.value = showFinger;
  _data = merged;
  try {
    try {
      await setUp(actor);
      list = steps;
    } on PlaybookStopped {
      // Reported below as a stopped run.
    } catch (e) {
      setUpFailure = PlaybookStepResult(
        title: 'Set up',
        status: PlaybookStepStatus.failed,
        elapsed: clock.elapsed - start,
        error: _describe(e),
      );
    }

    emit?.call({
      'event': 'start',
      'name': name,
      'speed': pace.name,
      'total': list.length,
      'depth': depth,
      'path': chain,
    });
    if (setUpFailure != null) {
      results.add(setUpFailure);
      _emitStep(emit, context, 0, list.length, setUpFailure, group: false);
    }

    results.addAll(
      await runSteps(
        actor,
        list,
        context,
        skipAll: setUpFailure != null || session.stopRequested,
      ),
    );

    final Duration tearDownStart = clock.elapsed;
    actor.ignoreStop = true;
    try {
      await tearDown(actor);
    } catch (e) {
      final PlaybookStepResult failure = PlaybookStepResult(
        title: 'Tear down',
        status: PlaybookStepStatus.failed,
        elapsed: clock.elapsed - tearDownStart,
        error: _describe(e),
      );
      results.add(failure);
      _emitStep(emit, context, 0, list.length, failure, group: false);
    } finally {
      actor.ignoreStop = false;
    }
  } finally {
    _data = null;
    if (fingerBefore != null) finger?.value = fingerBefore;
  }

  final PlaybookReport report = PlaybookReport(
    name: name,
    speed: pace.name,
    steps: results,
    elapsed: clock.elapsed - start,
    stopped: session.stopRequested,
  );
  emit?.call({
    'event': 'finish',
    'name': name,
    'speed': pace.name,
    'depth': depth,
    'path': chain,
    'passed': report.passed,
    'stopped': report.stopped,
    'ms': report.elapsed.inMilliseconds,
    if (depth == 0) 'report': report.toJson(),
  });
  return report;
}