Skip to main content
The UI extensions SDK provides utilities for testing your UI extensions, including rendering components, querying rendered output, mocking, triggering event handlers, waiting for asynchronous updates, and debugging the rendered component tree. To use these utilities, it’s recommended to use Vitest, but you can use any test runner you prefer. The code snippets on this page assume that Vitest is being used and reference Vitest globals such as describe() and expect().

Basic usage

The primary entry point is the createRenderer() function. This function requires an extension point location (e.g., 'crm.record.tab'), and returns a renderer object with a render() method and utilities for querying and interacting with the rendered output. Mocks for supported React hooks and the Extension Point API are automatically provided based on the specified extension point location.
Create a new renderer for each test to ensure test isolation. This prevents state from leaking between tests and makes your tests more reliable and maintainable.
The extension point location (e.g., 'crm.record.tab') is required because it determines the shape of the context, actions, and runServerlessFunction mocks that are automatically provided to your components during testing. Valid extension point locations include:
  • 'crm.record.tab'
  • 'crm.record.sidebar'
  • 'crm.preview'
  • 'helpdesk.sidebar'
  • 'settings'
  • 'home'

Querying components

findByTestId

findByTestId(component, testId) Finds a component by its testId prop. This is the recommended way to precisely find rendered components. This method throws an error if no match is found. The non-throwing variant is maybeFindByTestId().
It’s recommended to use test IDs because they provide a stable, explicit way to identify components in your tests. Unlike matchers based on props or text content, test IDs won’t break when you change the component’s appearance or behavior. This makes your tests more maintainable and less brittle.

find

find(component, matcher?) Finds the first descendant element that matches the given component and an optional matcher. Throws an error if no match is found. The non-throwing variant is maybeFind().

findAll

findAll(component, matcher?) Finds all descendant elements that match the given component and an optional matcher. Returns an array of all matching elements, or an empty array if no matches are found.

findChild

findChild(component, matcher?) Finds the first direct child element that matches the given component and an optional matcher. Only searches immediate children, not descendants. Throws an error if no match is found. The non-throwing variant is maybeFindChild().

findAllChildren

findAllChildren(component, matcher?) Finds all direct child elements that match the given component and an optional matcher. Only searches immediate children, not descendants. This combines the behavior of findAll() (returning all matches) with findChild() (searching only direct children).

maybeFind (non-throwing variants)

The following methods are non-throwing variants that return null if no match is found:
  • maybeFind(component, matcher?)
  • maybeFindChild(component, matcher?)
  • maybeFindByTestId(component, testId)

Element matchers

All query methods accept an optional parameter that can be either of the following:
  • Props object: match elements with specific prop values.
  • Predicate function: match elements using a custom function.

Interacting with components

Accessing element properties

Rendered elements expose several properties:

Triggering events

Use the trigger() method to invoke event handlers:

Async testing

Waiting for async updates

Use waitFor() to wait for asynchronous updates to the rendered output:

Timeout configuration

By default, waitFor() waits up to 1000ms. You can customize the timeout:
If the condition is not met within the timeout, a WaitForTimeoutError is thrown.

Mocking

The testing SDK provides comprehensive mocking support for UI extensions to avoid making real API calls and being dependent on HubSpot data. The testing utilities make it easy to control the behavior of provided React hooks, extension point actions, and more. When you create a renderer with createRenderer(extensionPointLocation), the testing SDK automatically creates appropriate mocks for the Extension Point API based on the location you specify. Default mock implementations with spies are provided for supported hooks such as useCrmProperties() and useAssociations() and Extension Point API actions such as actions.addAlert(). See the Mocking guide for detailed documentation on mocking, including:
  • Automatic mocks based on extension point location
  • Mocking context, actions, and runServerlessFunction
  • Available hook mocks (useCrmProperties(), useAssociations())
  • Advanced mock features (spy properties, resetting, etc.)

Working with fragments

Some components allow React nodes to be passed in as part of component props, and these props are treated as “fragment props.” Fragment props are automatically converted to RenderedFragmentNode objects:

Debugging

debugLog

Log a string representation of the rendered output:

toString

Get a string representation of a node:

getRootNode

Access the root node directly:

Type guards

The testing SDK provides type guard functions to help narrow TypeScript types when working with rendered nodes. These are useful when you need to perform type-specific operations on nodes.

isRenderedElementNode

Check if a node is a rendered element node:

isRenderedTextNode

Check if a node is a rendered text node:

isRenderedRootNode

Check if a node is a rendered root node:

isRenderedFragmentNode

Check if a node is a rendered fragment node:

isMatch

Check if a node matches a specific component and optional matcher criteria. This is particularly useful when iterating over child nodes where you need to check the type and properties of each node. The isMatch function is available in two ways:
  1. As a standalone exported function: useful for checking nodes when iterating over child nodes
  2. As a method on all rendered nodes: convenient for chaining from an already-found node

Standalone function

Node method

All rendered nodes (including RenderedRootNode, RenderedFragmentNode, RenderedElementNode, and RenderedTextNode) have an isMatch method:

Error handling

The testing SDK throws specific errors for different scenarios:
  • InvalidComponentsError: when invalid component types are detected in the rendered output.
  • WaitForTimeoutError: when a waitFor() check doesn’t pass within the timeout period.
  • MissingEventFunctionError: when triggering an event that doesn’t have a handler.
  • InvalidEventFunctionError: when triggering an event whose handler is not a function.
  • ComponentNotFoundByTestIdError: when a component with the specified testId is not found in the rendered output.
  • ComponentMismatchedByTestIdError: when a testId exists but the component type doesn’t match the expected component.
  • DuplicateTestIdError: when attempting to render multiple components with the same testId.
Last modified on March 29, 2026