execute method
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.',
);
}
}