androidx.a2ui.compose.ui.testing

Interfaces

A2uiComponentStub

Represents a stubbed or overridden Compose implementation for an A2UI component for testing.

A2uiTestController

A controller for orchestrating A2UI component and surface tests.

Composables

A2uiTestSurface

A test utility composable that mounts the root component of the specified A2uiSurfaceModel to allow testing individual components or entire surfaces in isolation.

Top-level functions summary

A2uiComponentPayload
A2uiComponentPayload(id: String, properties: Map<StringAny?>)

Creates an androidx.a2ui.model.protocol.A2uiComponentPayload for an ID-scoped test stub without requiring a dummy component type.

A2uiTestController
A2uiTestController(
    catalog: A2uiCatalog,
    theme: Map<StringAny?>,
    initialComponents: List<A2uiComponentPayload>,
    initialData: Map<StringAny?>,
    componentStubs: List<A2uiComponentStub>
)

Creates a A2uiTestController that can be used to drive tests.

Extension functions summary

inline T?

Reads a value from the underlying data model at the specified JSON pointer path and casts it to the requested type T.

Top-level functions

A2uiComponentPayload

fun A2uiComponentPayload(id: String, properties: Map<StringAny?> = emptyMap()): A2uiComponentPayload

Creates an androidx.a2ui.model.protocol.A2uiComponentPayload for an ID-scoped test stub without requiring a dummy component type.

This overload is strictly reserved for components stubbed via A2uiComponentStub.withId.

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.a2ui.model.protocol.A2uiComponentPayload
import androidx.compose.foundation.text.BasicText
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.v2.runComposeUiTest

runComposeUiTest {
    val textProp = A2uiProperty.string("text")
    val stub =
        A2uiComponentStub.withId("title_stub") { props, modifier ->
            val title = props[textProp] ?: ""
            BasicText(title, modifier = modifier.testTag("title_tag"))
        }

    // Use the ID-only A2uiComponentPayload helper specifically designed for ID stubs
    val initialPayload =
        A2uiComponentPayload(id = "title_stub", properties = mapOf("text" to "Welcome to A2UI"))

    val controller =
        A2uiTestController(
            catalog = A2uiCatalog("test_catalog", emptyList()),
            initialComponents = listOf(initialPayload),
            componentStubs = listOf(stub),
        )
    val surface = controller.start()

    setContent { A2uiTestSurface(surface = surface) }

    onNodeWithTag("title_tag").assertIsDisplayed()
    onNodeWithText("Welcome to A2UI").assertIsDisplayed()
}
Parameters
id: String

The unique identifier of the stubbed component.

properties: Map<StringAny?> = emptyMap()

The initial property map to configure the stub.

Returns
A2uiComponentPayload

An A2uiComponentPayload configured for the ID stub.

Throws
IllegalArgumentException

If used for a component ID that has not been registered as an ID stub via A2uiComponentStub.withId.

fun A2uiTestController(
    catalog: A2uiCatalog,
    theme: Map<StringAny?> = emptyMap(),
    initialComponents: List<A2uiComponentPayload> = emptyList(),
    initialData: Map<StringAny?> = emptyMap(),
    componentStubs: List<A2uiComponentStub> = emptyList()
): A2uiTestController

Creates a A2uiTestController that can be used to drive tests.

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
catalog: A2uiCatalog

The A2uiCatalog under test.

theme: Map<StringAny?> = emptyMap()

Simulated theme overrides (e.g., primaryColor) from the agent.

initialComponents: List<A2uiComponentPayload> = emptyList()

A list of A2uiComponentPayloads used to initialize the UI hierarchy.

initialData: Map<StringAny?> = emptyMap()

The initial data tree injected into the data model.

componentStubs: List<A2uiComponentStub> = emptyList()

A List of component stubs to override or append to the catalog.

Returns
A2uiTestController

An unstarted A2uiTestController.

Extension functions

A2uiTestController.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 T.

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: String

The JSON pointer path to read (e.g., "/user/name").

Returns
T?

The data model value cast to T, or null if the path does not exist.

Throws
ClassCastException

if the value at path cannot be cast to T.