# A2uiTestController

Artifact: [androidx.a2ui.compose:compose-ui-testing](https://developer.android.com/jetpack/androidx/releases/a2ui-compose) [View Source](https://cs.android.com/search?q=file:androidx/a2ui/compose/ui/testing/A2uiTestController.kt+class:androidx.a2ui.compose.ui.testing.A2uiTestController) Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)


```
interface A2uiTestController
```

<br />

*** ** * ** ***

A controller for orchestrating A2UI component and surface tests.

It provides access to the underlying `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` for rendering, tracks outbound actions dispatched by the UI, and exposes synchronous methods to simulate incoming protocol messages (data and component updates) from an agent.

```kotlin
import androidx.a2ui.compose.runtime.A2uiProperty
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val titleProp = A2uiProperty.string("title")
    val rootStub =
        A2uiComponentStub.withId("root") { props, modifier ->
            val title = props[titleProp] ?: "Initial Title"
            BasicText(title, modifier = modifier.testTag("root_tag"))
        }

    // Initialize the controller with catalog and stubs
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("root")),
            componentStubs = listOf(rootStub),
        )

    // Start message processing and initialize a surface model
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithText("Initial Title").assertIsDisplayed()

    // Simulate an agent pushing an incremental property update to the component
    controller.updateComponent(id = "root", properties = mapOf("title" to "Updated Title"))
    // Suspend until the update components message is fully processed
    controller.waitForIdle()

    onNodeWithText("Updated Title").assertIsDisplayed()
}
```

## Summary

| ### Public functions |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearDispatchedActions()()` Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#dispatchedActions()`. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearOutboundErrors()()` Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundErrors()`. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearOutboundEvents()()` Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundEvents()`. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#failComponent(kotlin.String,androidx.a2ui.model.protocol.A2uiException)(id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, exception: https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiException)` Simulates an agent hallucination or validation failure for a given component. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#getRawData(kotlin.String)(path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html)` Reads a value synchronously from the underlying test data model. |
| `suspend https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#start()()` Starts the controller's background processing loops, initializes the surface with the configured theme, initial data, and initial components, and waits for the reactive state to settle. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#updateComponent(kotlin.String,kotlin.collections.Map)(id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, properties: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-map/index.html<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?>)` Simulates the agent pushing an incremental property update to an already existing component on the surface, reusing its previously recorded type. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#updateComponent(kotlin.String,kotlin.String,kotlin.collections.Map)(id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, type: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, properties: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-map/index.html<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?>)` Simulates the agent pushing a structural component update to the surface. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#updateData(kotlin.String,kotlin.Any)(path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, value: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?)` Simulates the agent sending a data model update to the test surface. |
| `suspend https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/index.html` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#waitForIdle()()` Suspends test execution until the coroutine scheduler processes all pending background data layer tasks and evaluator actions. |

| ### Public properties |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiUserAction>` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#dispatchedActions()` A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiUserAction`s dispatched by components during the test. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientErrorMessage>` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundErrors()` A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientErrorMessage`s queued for transmission to the server (agent), useful for asserting self-correction feedback loops. |
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-list/index.html<https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientEventMessage>` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundEvents()` A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientEventMessage`s queued for transmission to the server (agent). |
| `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` | `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#surface()` The `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` instance managed by this controller. |

| ### Extension functions |
|---|---|
| `inline T?` | `<T : https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?> https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController.https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)(path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html)` Reads a value from the underlying data model at the specified JSON pointer path and casts it to the requested type `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/package-summary#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)`. |

## Public functions

### clearDispatchedActions

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun clearDispatchedActions(): Unit
```

Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#dispatchedActions()`.

### clearOutboundErrors

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun clearOutboundErrors(): Unit
```

Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundErrors()`.

### clearOutboundEvents

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun clearOutboundEvents(): Unit
```

Clears the history of `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundEvents()`.

### failComponent

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun failComponent(id: String, exception: A2uiException): Unit
```

Simulates an agent hallucination or validation failure for a given component.

| Parameters |
|---|---|
| `id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The unique identifier of the component to fail. |
| `exception: https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiException` | The `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiException` simulating the error. |

### getRawData

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun getRawData(path: String): Any?
```

Reads a value synchronously from the underlying test data model.

| Parameters |
|---|---|
| `path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The JSON pointer path to read. |

| Returns |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?` | The raw data model value, or `null` if the path does not exist. |

### start

```
suspend fun start(): A2uiSurfaceModel
```

Starts the controller's background processing loops, initializes the surface with the configured theme, initial data, and initial components, and waits for the reactive state to settle.

This is expected to be called in a block passed to `runComposeUiTest`.

```kotlin
import androidx.a2ui.compose.runtime.A2uiProperty
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val titleProp = A2uiProperty.string("title")
    val rootStub =
        A2uiComponentStub.withId("root") { props, modifier ->
            val title = props[titleProp] ?: "Initial Title"
            BasicText(title, modifier = modifier.testTag("root_tag"))
        }

    // Initialize the controller with catalog and stubs
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("root")),
            componentStubs = listOf(rootStub),
        )

    // Start message processing and initialize a surface model
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithText("Initial Title").assertIsDisplayed()

    // Simulate an agent pushing an incremental property update to the component
    controller.updateComponent(id = "root", properties = mapOf("title" to "Updated Title"))
    // Suspend until the update components message is fully processed
    controller.waitForIdle()

    onNodeWithText("Updated Title").assertIsDisplayed()
}
```

| Returns |
|---|---|
| `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` | The fully initialized `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` ready to be mounted in a UI. |

### updateComponent

```
fun updateComponent(id: String, properties: Map<String, Any?>): Unit
```

Simulates the agent pushing an incremental property update to an already existing component on the surface, reusing its previously recorded type.

```kotlin
import androidx.a2ui.compose.runtime.A2uiProperty
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val titleProp = A2uiProperty.string("title")
    val rootStub =
        A2uiComponentStub.withId("root") { props, modifier ->
            val title = props[titleProp] ?: "Initial Title"
            BasicText(title, modifier = modifier.testTag("root_tag"))
        }

    // Initialize the controller with catalog and stubs
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("root")),
            componentStubs = listOf(rootStub),
        )

    // Start message processing and initialize a surface model
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithText("Initial Title").assertIsDisplayed()

    // Simulate an agent pushing an incremental property update to the component
    controller.updateComponent(id = "root", properties = mapOf("title" to "Updated Title"))
    // Suspend until the update components message is fully processed
    controller.waitForIdle()

    onNodeWithText("Updated Title").assertIsDisplayed()
}
```

| Parameters |
|---|---|
| `id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The unique identifier of the component. |
| `properties: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-map/index.html<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?>` | The property map representing the component's configuration. |

| Throws |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-illegal-state-exception/index.html` | If no type has been recorded for `id` via `initialComponents` or a prior call to `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#updateComponent(kotlin.String,kotlin.collections.Map)`. |

### updateComponent

```
fun updateComponent(id: String, type: String, properties: Map<String, Any?>): Unit
```

Simulates the agent pushing a structural component update to the surface.

```kotlin
import androidx.a2ui.compose.runtime.A2uiProperty
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val titleProp = A2uiProperty.string("title")
    val rootStub =
        A2uiComponentStub.withId("root") { props, modifier ->
            val title = props[titleProp] ?: "Initial Title"
            BasicText(title, modifier = modifier.testTag("root_tag"))
        }

    // Initialize the controller with catalog and stubs
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("root")),
            componentStubs = listOf(rootStub),
        )

    // Start message processing and initialize a surface model
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithText("Initial Title").assertIsDisplayed()

    // Simulate an agent pushing an incremental property update to the component
    controller.updateComponent(id = "root", properties = mapOf("title" to "Updated Title"))
    // Suspend until the update components message is fully processed
    controller.waitForIdle()

    onNodeWithText("Updated Title").assertIsDisplayed()
}
```

| Parameters |
|---|---|
| `id: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The unique identifier of the component. |
| `type: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The string type identifier of the component. |
| `properties: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/-map/index.html<https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html, https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?>` | The property map representing the component's configuration. |

### updateData

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
fun updateData(path: String, value: Any?): Unit
```

Simulates the agent sending a data model update to the test surface.

```kotlin
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.getData
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialData = mapOf("user" to mapOf("name" to "Alice")),
        )
    controller.start()

    // Read typed data from the controller's underlying data model
    val initialName: String? = controller.getData("/user/name")
    assertThat(initialName).isEqualTo("Alice")

    // Simulate the agent sending a data layer update
    controller.updateData("/user/name", "Bob")
    controller.waitForIdle()

    val updatedName: String? = controller.getData("/user/name")
    assertThat(updatedName).isEqualTo("Bob")
}
```

| Parameters |
|---|---|
| `path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The JSON pointer path to update. |
| `value: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-any/index.html?` | The new value to place at the specified path. |

### waitForIdle

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
suspend fun waitForIdle(): Unit
```

Suspends test execution until the coroutine scheduler processes all pending background data layer tasks and evaluator actions.

Note: This settles the A2UI data model and component registry layers. Typically, calling `ComposeUiTest.waitForIdle()` afterward is still needed to allow the Compose UI tree to recompose and reflect the new states.

This is expected to be called in a block passed to `runComposeUiTest`.

```kotlin
import androidx.a2ui.compose.runtime.A2uiProperty
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val titleProp = A2uiProperty.string("title")
    val rootStub =
        A2uiComponentStub.withId("root") { props, modifier ->
            val title = props[titleProp] ?: "Initial Title"
            BasicText(title, modifier = modifier.testTag("root_tag"))
        }

    // Initialize the controller with catalog and stubs
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("root")),
            componentStubs = listOf(rootStub),
        )

    // Start message processing and initialize a surface model
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithText("Initial Title").assertIsDisplayed()

    // Simulate an agent pushing an incremental property update to the component
    controller.updateComponent(id = "root", properties = mapOf("title" to "Updated Title"))
    // Suspend until the update components message is fully processed
    controller.waitForIdle()

    onNodeWithText("Updated Title").assertIsDisplayed()
}
```

## Public properties

### dispatchedActions

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
val dispatchedActions: List<A2uiUserAction>
```

A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiUserAction`s dispatched by components during the test. This includes both local client-side function calls and server-bound events.

To inspect only the events queued for network transmission to the agent, use `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#outboundEvents()`. Use `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearDispatchedActions()` to clear this record.

```kotlin
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.clickable
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val buttonStub =
        A2uiComponentStub.withId("button") { _, modifier ->
            BasicText(
                "Submit",
                modifier.testTag("submit_button").clickable {
                    dispatchAction(
                        mapOf(
                            "event" to
                                mapOf("name" to "on_submit", "context" to emptyMap<String, Any?>())
                        )
                    )
                },
            )
        }

    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("button")),
            componentStubs = listOf(buttonStub),
        )
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    // Perform a click on the rendered button
    onNodeWithTag("submit_button").performClick()
    waitForIdle()
    controller.waitForIdle()

    // Assert that the dispatched event was recorded by the controller
    assertThat(controller.dispatchedActions).hasSize(1)
    assertThat(controller.outboundEvents).hasSize(1)
    assertThat(controller.outboundEvents.first().type).isEqualTo("on_submit")
}
```

### outboundErrors

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
val outboundErrors: List<A2uiClientErrorMessage>
```

A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientErrorMessage`s queued for transmission to the server (agent), useful for asserting self-correction feedback loops.

Use `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearOutboundErrors()` to clear this record.

### outboundEvents

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
val outboundEvents: List<A2uiClientEventMessage>
```

A sequentially ordered record of all `https://developer.android.com/reference/kotlin/androidx/a2ui/model/protocol/A2uiClientEventMessage`s queued for transmission to the server (agent).

Local function calls are not included in this list. Use `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#clearOutboundEvents()` to clear this record.

```kotlin
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiComponentPayload
import androidx.a2ui.compose.ui.testing.A2uiComponentStub
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.A2uiTestSurface
import androidx.compose.foundation.clickable
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val buttonStub =
        A2uiComponentStub.withId("button") { _, modifier ->
            BasicText(
                "Submit",
                modifier.testTag("submit_button").clickable {
                    dispatchAction(
                        mapOf(
                            "event" to
                                mapOf("name" to "on_submit", "context" to emptyMap<String, Any?>())
                        )
                    )
                },
            )
        }

    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(A2uiComponentPayload("button")),
            componentStubs = listOf(buttonStub),
        )
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    // Perform a click on the rendered button
    onNodeWithTag("submit_button").performClick()
    waitForIdle()
    controller.waitForIdle()

    // Assert that the dispatched event was recorded by the controller
    assertThat(controller.dispatchedActions).hasSize(1)
    assertThat(controller.outboundEvents).hasSize(1)
    assertThat(controller.outboundEvents.first().type).isEqualTo("on_submit")
}
```

### surface

Added in [1.0.0-alpha02](https://developer.android.com/jetpack/androidx/releases/a2ui-compose#1.0.0-alpha02)

```
val surface: A2uiSurfaceModel
```

The `https://developer.android.com/reference/kotlin/androidx/a2ui/model/processor/A2uiSurfaceModel` instance managed by this controller.

| Throws |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-illegal-state-exception/index.html` | If accessed before calling `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/A2uiTestController#start()`. |

## Extension functions

### A2uiTestController.getData

Artifact: [androidx.a2ui.compose:compose-ui-testing](https://developer.android.com/jetpack/androidx/releases/a2ui-compose) [View Source](https://cs.android.com/search?q=file:androidx/a2ui/compose/ui/testing/A2uiTestController.kt+function:getData)

```
inline fun <T : Any?> A2uiTestController.getData(path: String): T?
```

Reads a value from the underlying data model at the specified JSON pointer path and casts it to the requested type `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/package-summary#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)`.

```kotlin
import androidx.a2ui.compose.ui.A2uiCatalog
import androidx.a2ui.compose.ui.testing.A2uiTestController
import androidx.a2ui.compose.ui.testing.getData
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialData = mapOf("user" to mapOf("name" to "Alice")),
        )
    controller.start()

    // Read typed data from the controller's underlying data model
    val initialName: String? = controller.getData("/user/name")
    assertThat(initialName).isEqualTo("Alice")

    // Simulate the agent sending a data layer update
    controller.updateData("/user/name", "Bob")
    controller.waitForIdle()

    val updatedName: String? = controller.getData("/user/name")
    assertThat(updatedName).isEqualTo("Bob")
}
```

| Parameters |
|---|---|
| `path: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-string/index.html` | The JSON pointer path to read (e.g., `"/user/name"`). |

| Returns |
|---|---|
| `T?` | The data model value cast to `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/package-summary#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)`, or `null` if the path does not exist. |

| Throws |
|---|---|
| `https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-class-cast-exception/index.html` | if the value at `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/package-summary#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)` cannot be cast to `https://developer.android.com/reference/kotlin/androidx/a2ui/compose/ui/testing/package-summary#(androidx.a2ui.compose.ui.testing.A2uiTestController).getData(kotlin.String)`. |