invokeSemanticAction method

Future<SemanticActionInvocationResult> invokeSemanticAction(
  1. SemanticAction action, {
  2. SemanticNode? node,
  3. SemanticNodeId? id,
  4. SemanticRole? role,
  5. String? label,
  6. Object? value,
  7. Object? payload,
  8. bool? focused,
  9. bool? selected,
  10. bool? enabled,
  11. bool? checked,
  12. bool? busy,
  13. String? validationError,
})

Invokes a semantic action on a node in the current semantic tree.

When node is omitted, the remaining filters must identify exactly one node before checking action availability. Tests can exercise app commands, controls, fields, and app-authored regions by role/label/action instead of reaching through widget internals.

Requested frames continue while the handler is pending, without advancing test time. Dialog-opening handlers still need an explicit answer.

For SemanticAction.setValue, pass the value to apply as payload (the value argument is a node filter, not the payload).

Implementation

Future<SemanticActionInvocationResult> invokeSemanticAction(
  SemanticAction action, {
  SemanticNode? node,
  SemanticNodeId? id,
  SemanticRole? role,
  String? label,
  Object? value,
  Object? payload,
  bool? focused,
  bool? selected,
  bool? enabled,
  bool? checked,
  bool? busy,
  String? validationError,
}) async {
  _assertNotDisposed('invokeSemanticAction');
  final root = _root;
  if (root == null) {
    return SemanticActionInvocationResult.notFound(action);
  }

  final tree = semantics();
  final SemanticNode target;
  try {
    target = _resolveSemanticActionTarget(
      tree,
      node: node,
      id: id,
      role: role,
      label: label,
      value: value,
      focused: focused,
      selected: selected,
      enabled: enabled,
      checked: checked,
      busy: busy,
      validationError: validationError,
    );
  } on SemanticQueryError catch (error) {
    return error.matchCount == 0
        ? SemanticActionInvocationResult.notFound(action, error: error)
        : SemanticActionInvocationResult.ambiguous(action, error: error);
  }
  if (!target.enabled) {
    return SemanticActionInvocationResult.disabled(target, action);
  }
  if (!target.actions.contains(action)) {
    return SemanticActionInvocationResult.unsupported(target, action);
  }

  // Dispatch through the same map-based path the live wire uses, so the tester
  // can't pass where production fails (the divergence that previously hid a
  // cross-fire bug). The tree from `semantics()` carries the id→element map.
  final result = await _runAction(
    () => invokeSemanticActionFromElement(
      tree: tree,
      id: target.id,
      action: action,
      value: payload,
    ),
  );
  _owner.flushBuild();
  return result;
}