or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-react.mdcss-styling.mddom-integration.mderror-boundaries.mdevent-system.mdindex.mdreact-hooks.mdrouting.mdserver-side-rendering.mdtesting-utilities.md
tile.json

testing-utilities.mddocs/

Testing Utilities

Comprehensive testing utilities including component type checking, event simulation, DOM utilities, and container management for React component testing.

Test Control Functions

/* Synchronous test wrapper */
let act: (unit => unit) => unit;

/* Asynchronous test wrapper */
let actAsync: (unit => Js.Promise.t('a)) => Js.Promise.t(unit);

Component Type Checking

[@bs.module "react-dom/test-utils"]
external isElement: 'element => bool = "isElement";

[@bs.module "react-dom/test-utils"]
external isElementOfType: ('element, React.component('props)) => bool = "isElementOfType";

[@bs.module "react-dom/test-utils"]
external isDOMComponent: 'element => bool = "isDOMComponent";

[@bs.module "react-dom/test-utils"]
external isCompositeComponent: 'element => bool = "isCompositeComponent";

[@bs.module "react-dom/test-utils"]
external isCompositeComponentWithType: ('element, React.component('props)) => bool = "isCompositeComponentWithType";

Event Simulation

module Simulate = {
  /* Basic events */
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external click: Dom.element => unit = "click";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external clickWithEvent: (Dom.element, 'event) => unit = "click";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external change: Dom.element => unit = "change";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external changeWithEvent: (Dom.element, 'event) => unit = "change";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external blur: Dom.element => unit = "blur";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external focus: Dom.element => unit = "focus";
  
  /* Form helpers */
  let changeWithValue: (Dom.element, string) => unit;
  let changeWithChecked: (Dom.element, bool) => unit;
  
  /* Media events */
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external canPlay: Dom.element => unit = "canPlay";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external timeUpdate: Dom.element => unit = "timeUpdate";
  
  [@bs.module "react-dom/test-utils"] [@bs.scope "Simulate"]
  external ended: Dom.element => unit = "ended";
};

DOM Utilities

module DOM = {
  [@bs.return nullable] [@bs.get]
  external value: Dom.element => option(string) = "value";

  let findBySelector: (Dom.element, string) => option(Dom.element);
  let findByAllSelector: (Dom.element, string) => array(Dom.element);
  let findBySelectorAndTextContent: (Dom.element, string, string) => option(Dom.element);
  let findBySelectorAndPartialTextContent: (Dom.element, string, string) => option(Dom.element);
};

Container Management

let prepareContainer: (ref(option(Dom.element)), unit) => unit;
let cleanupContainer: (ref(option(Dom.element)), unit) => unit;
let getContainer: ref(option(Dom.element)) => Dom.element;

Usage Examples

Basic Component Testing

/* Test a simple button component */
let testButton = () => {
  let container = ref(None);
  ReactTestUtils.prepareContainer(container, ());
  
  let buttonElement = ReactDOM.render(
    <button onClick={_ => Js.log("clicked")}>
      {React.string("Click me")}
    </button>,
    ReactTestUtils.getContainer(container)
  );
  
  /* Test that it's a DOM component */
  let isDOMElement = ReactTestUtils.isDOMComponent(buttonElement);
  assert(isDOMElement);
  
  /* Simulate click */
  ReactTestUtils.Simulate.click(buttonElement);
  
  ReactTestUtils.cleanupContainer(container, ());
};

Form Testing

[@react.component]
let make = () => {
  let (value, setValue) = React.useState(() => "");
  let (submitted, setSubmitted) = React.useState(() => false);
  
  let handleSubmit = (event) => {
    ReactEvent.Form.preventDefault(event);
    setSubmitted(_ => true);
  };
  
  <form onSubmit=handleSubmit data-testid="test-form">
    <input 
      value 
      onChange={(e) => setValue(_ => ReactEvent.Form.target(e)##value)}
      data-testid="test-input"
    />
    <button type_="submit" data-testid="submit-button">
      {React.string("Submit")}
    </button>
    {submitted ? <div data-testid="success"> {React.string("Submitted!")} </div> : React.null}
  </form>
};

let testForm = () => {
  let container = ref(None);
  ReactTestUtils.prepareContainer(container, ());
  
  ReactTestUtils.act(() => {
    ReactDOM.render(<TestForm />, ReactTestUtils.getContainer(container));
  });
  
  let containerElement = ReactTestUtils.getContainer(container);
  
  /* Find elements */
  let inputElement = ReactTestUtils.DOM.findBySelector(containerElement, "[data-testid='test-input']");
  let buttonElement = ReactTestUtils.DOM.findBySelector(containerElement, "[data-testid='submit-button']");
  
  switch (inputElement, buttonElement) {
  | (Some(input), Some(button)) => {
      /* Test input change */
      ReactTestUtils.act(() => {
        ReactTestUtils.Simulate.changeWithValue(input, "test value");
      });
      
      /* Verify input value */
      let inputValue = ReactTestUtils.DOM.value(input);
      assert(inputValue === Some("test value"));
      
      /* Test form submission */
      ReactTestUtils.act(() => {
        ReactTestUtils.Simulate.click(button);
      });
      
      /* Check for success message */
      let successElement = ReactTestUtils.DOM.findBySelector(containerElement, "[data-testid='success']");
      assert(Option.isSome(successElement));
    }
  | _ => assert(false)
  };
  
  ReactTestUtils.cleanupContainer(container, ());
};

Async Testing

let testAsyncComponent = () => {
  let container = ref(None);
  ReactTestUtils.prepareContainer(container, ());
  
  ReactTestUtils.actAsync(() => {
    ReactDOM.render(<AsyncComponent />, ReactTestUtils.getContainer(container));
    Promise.resolve();
  })
  |> Promise.then_(() => {
       let containerElement = ReactTestUtils.getContainer(container);
       let loadedContent = ReactTestUtils.DOM.findBySelectorAndTextContent(
         containerElement, 
         ".content", 
         "Loaded data"
       );
       assert(Option.isSome(loadedContent));
       
       ReactTestUtils.cleanupContainer(container, ());
       Promise.resolve();
     });
};

Component Type Testing

[@react.component]
let make = (~title: string) => {
  <h1> {React.string(title)} </h1>
};

let testComponentTypes = () => {
  let container = ref(None);
  ReactTestUtils.prepareContainer(container, ());
  
  let titleComponent = ReactDOM.render(
    <TitleComponent title="Test Title" />, 
    ReactTestUtils.getContainer(container)
  );
  
  /* Test component type */
  let isCorrectType = ReactTestUtils.isElementOfType(titleComponent, TitleComponent.make);
  assert(isCorrectType);
  
  let isComposite = ReactTestUtils.isCompositeComponent(titleComponent);
  assert(isComposite);
  
  ReactTestUtils.cleanupContainer(container, ());
};