execute method

Future<void> execute(
  1. TestStep step
)

Implementation

Future<void> execute(TestStep step) async {
  if (step.type == 'group') {
    for (final nested in step.nestedSteps) {
      await execute(nested);
    }
    return;
  }
  if (step.type == 'repeat') {
    final times = step.args['times'] as int? ?? 1;
    for (var i = 0; i < times; i++) {
      for (final nested in step.nestedSteps) {
        await execute(nested);
      }
    }
    return;
  }
  if (step.type == 'ifVisible') {
    final id = step.args['id']?.toString();
    if (id == null || id.isEmpty) {
      throw EnsembleTestFailure('ifVisible requires "id"');
    }
    if (assertions.finderForId(id).evaluate().isNotEmpty) {
      for (final nested in step.nestedSteps) {
        await execute(nested);
      }
    }
    return;
  }
  if (step.type == 'optional') {
    try {
      for (final nested in step.nestedSteps) {
        await execute(nested);
      }
    } on EnsembleTestFailure {
      // Best-effort steps (e.g. dismiss cookie banner).
    }
    return;
  }

  switch (step.type) {
    case 'mocks':
      _applyMocks(step.mocks);
      return;
    case 'httpRequest':
      await HttpRequestAction.execute(step.args);
      return;
    case 'wait':
      final durationMs = step.args['durationMs'] as int? ?? 500;
      await tester.runAsync(() async {
        await Future<void>.delayed(Duration(milliseconds: durationMs));
      });
      await _pump(label: 'wait');
      return;
    case 'waitForText':
      await _waitFor(
        step: step,
        text: step.args['text']?.toString(),
        anyOf: _stringListArg(step.args['anyOf']),
        timeoutMs: step.args['timeoutMs'] as int? ??
            config.defaultWaitTimeout.inMilliseconds,
      );
      return;
    case 'waitForGone':
      await _waitForGone(
        id: step.args['id']?.toString(),
        timeoutMs: step.args['timeoutMs'] as int? ??
            config.defaultWaitTimeout.inMilliseconds,
      );
      return;
    case 'waitForApi':
      await _waitForApi(
        name: step.args['name']?.toString(),
        times: step.args['times'] as int? ?? 1,
        timeoutMs: step.args['timeoutMs'] as int? ??
            config.defaultWaitTimeout.inMilliseconds,
      );
      return;
    case 'waitForNavigation':
      final screen = step.args['screen']?.toString();
      if (screen == null) {
        throw EnsembleTestFailure('waitForNavigation requires "screen"');
      }
      await _waitForNavigation(
        step: step,
        screen: screen,
        timeoutMs: step.args['timeoutMs'] as int? ??
            config.defaultWaitTimeout.inMilliseconds,
      );
      return;
    case 'expectScreen':
      final screen =
          step.args['name']?.toString() ?? step.args['screen']?.toString();
      if (screen == null) {
        throw EnsembleTestFailure('expectScreen requires "name" or "screen"');
      }
      assertions.expectNavigateTo(screen);
      return;
  }

  final canonical = TestStepVocabulary.resolveStepType(step.type);
  if (canonical != step.type) {
    return execute(step.withCanonicalType(canonical));
  }

  switch (step.type) {
    case 'openScreen':
      await _openScreen(step);
      break;
    case 'tap':
      await _tap(
        _requireId(step),
        timeoutMs: step.args['timeoutMs'] as int?,
        step: step,
      );
      break;
    case 'enterText':
      await _enterText(
        _requireId(step),
        step.args['value']?.toString() ?? '',
        submit: step.args['submit'] == true,
      );
      await onAfterActionStep?.call(step);
      break;
    case 'clearText':
      await _clearText(_requireId(step));
      await onAfterActionStep?.call(step);
      break;
    case 'replaceText':
      await _clearText(_requireId(step));
      await _enterText(
        _requireId(step),
        step.args['value']?.toString() ?? '',
        submit: step.args['submit'] == true,
      );
      await onAfterActionStep?.call(step);
      break;
    case 'submitText':
      await _submitText(_requireId(step));
      break;
    case 'select':
      await _select(_requireId(step), step.args['value']?.toString());
      break;
    case 'toggle':
      await _toggle(_requireId(step));
      break;
    case 'waitFor':
      await _waitFor(
        step: step,
        id: step.args['id']?.toString(),
        text: step.args['text']?.toString(),
        anyOf: _stringListArg(step.args['anyOf']),
        timeoutMs: step.args['timeoutMs'] as int? ??
            config.defaultWaitTimeout.inMilliseconds,
      );
      break;
    case 'pump':
      await _pump(
        duration: Duration(
          milliseconds: step.args['durationMs'] as int? ??
              config.waitPollInterval.inMilliseconds,
        ),
        label: 'pump',
      );
      break;
    case 'settle':
      await _settle(
        timeout: step.args['timeoutMs'] != null
            ? Duration(milliseconds: step.args['timeoutMs'] as int)
            : null,
      );
      break;
    case 'scrollUntilVisible':
      await _scrollUntilVisible(_requireId(step));
      break;
    case 'expectVisible':
      assertions.expectVisible(_requireId(step));
      break;
    case 'expectNotVisible':
      assertions.expectNotVisible(_requireId(step));
      break;
    case 'expectText':
      final anyOf = _stringListArg(step.args['anyOf']);
      final text = step.args['text']?.toString();
      if (anyOf.isNotEmpty) {
        assertions.expectTextAny(anyOf);
      } else if (text != null) {
        assertions.expectText(text);
      } else {
        throw EnsembleTestFailure('expectText requires "text" or "anyOf"');
      }
      break;
    case 'expectNoText':
      final anyOf = _stringListArg(step.args['anyOf']);
      final text = step.args['text']?.toString();
      if (anyOf.isNotEmpty) {
        assertions.expectNoTextAny(anyOf);
      } else if (text != null) {
        assertions.expectNoText(text);
      } else {
        throw EnsembleTestFailure('expectNoText requires "text" or "anyOf"');
      }
      break;
    case 'expectEnabled':
      assertions.expectEnabled(_requireId(step));
      break;
    case 'expectDisabled':
      assertions.expectDisabled(_requireId(step));
      break;
    case 'expectValue':
      assertions.expectValue(_requireId(step), step.args['equals']);
      break;
    case 'expectApiCalled':
      final name = step.args['name']?.toString();
      if (name == null) {
        throw EnsembleTestFailure('expectApiCalled requires "name"');
      }
      assertions.expectApiCalled(name, step.args['times'] as int? ?? 1);
      break;
    case 'expectApiNotCalled':
      final name = step.args['name']?.toString();
      if (name == null) {
        throw EnsembleTestFailure('expectApiNotCalled requires "name"');
      }
      assertions.expectApiNotCalled(name);
      break;
    case 'expectCount':
      final expected = step.args['equals'] as int?;
      if (expected == null) {
        throw EnsembleTestFailure('expectCount requires "equals"');
      }
      assertions.expectCount(_requireId(step), expected);
      break;
    case 'expectNavigateTo':
      final screen = step.args['screen']?.toString();
      if (screen == null) {
        throw EnsembleTestFailure('expectNavigateTo requires "screen"');
      }
      assertions.expectNavigateTo(screen);
      break;
    case 'expectVisited':
      final screen = step.args['screen']?.toString();
      if (screen == null) {
        throw EnsembleTestFailure('expectVisited requires "screen"');
      }
      assertions.expectVisited(screen);
      break;
    case 'expectStorage':
      final key = step.args['key']?.toString();
      if (key == null) {
        throw EnsembleTestFailure('expectStorage requires "key"');
      }
      assertions.expectStorage(key, step.args['equals']);
      break;
    case 'setStorage':
      final key = step.args['key']?.toString();
      if (key == null) {
        throw EnsembleTestFailure('setStorage requires "key"');
      }
      context.setStorage(key, step.args['value']);
      break;
    case 'setEnv':
      final key = step.args['key']?.toString();
      if (key == null) {
        throw EnsembleTestFailure('setEnv requires "key"');
      }
      context.setEnv(key, step.args['value']);
      break;
    case 'resetApiCalls':
      context.apiOverlay.resetCalls();
      break;
    case 'logApiCalls':
      final path = await tester.runAsync(() {
        return writeApiCallsLog(context);
      });
      context.logger.log('apiCalls: $path');
      break;
    default:
      if (await ExtendedStepHandlers.tryExecute(this, step)) {
        return;
      }
      throw EnsembleTestFailure(
        'Unknown test step: ${step.type}. See STEP_VOCABULARY.md for supported steps.',
      );
  }
}