CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-testing-library--react-native

Simple and complete React Native testing utilities that encourage good testing practices

Pending
Overview
Eval results
Files

matchers.mddocs/

Jest Matchers

Extended Jest matchers specifically designed for React Native component testing and assertions. These matchers integrate with Jest's expect API to provide intuitive and readable assertions for React Native elements.

Capabilities

Visibility and Presence Matchers

Matchers for testing element visibility and presence in the component tree.

/**
 * Assert element is currently on the screen (in the rendered tree)
 */
expect(element).toBeOnTheScreen(): void;
expect(element).not.toBeOnTheScreen(): void;

/**
 * Assert element is visible to users (considering accessibility and style)  
 */
expect(element).toBeVisible(): void;
expect(element).not.toBeVisible(): void;

/**
 * Assert element is empty (has no children)
 */
expect(element).toBeEmptyElement(): void;
expect(element).not.toBeEmptyElement(): void;

Usage Examples:

import { render, screen } from "@testing-library/react-native";

test("visibility matchers", () => {
  render(
    <View>
      <Text testID="visible-text">Visible text</Text>
      <View testID="empty-container" />
      <Text 
        testID="hidden-text" 
        style={{ opacity: 0 }}
      >
        Hidden text
      </Text>
      <View testID="accessibility-hidden" accessibilityElementsHidden>
        <Text>Hidden from accessibility</Text>
      </View>
    </View>
  );

  const visibleText = screen.getByTestId("visible-text");  
  const emptyContainer = screen.getByTestId("empty-container");
  const hiddenText = screen.getByTestId("hidden-text");
  const a11yHidden = screen.getByTestId("accessibility-hidden");

  // Element presence
  expect(visibleText).toBeOnTheScreen();
  expect(hiddenText).toBeOnTheScreen(); // Still in tree, just not visible

  // Element visibility  
  expect(visibleText).toBeVisible();
  expect(hiddenText).not.toBeVisible(); // Has opacity: 0
  expect(a11yHidden).not.toBeVisible(); // Hidden from accessibility

  // Empty elements
  expect(emptyContainer).toBeEmptyElement();
  expect(visibleText).not.toBeEmptyElement(); // Has text content
});

test("conditional rendering", () => {
  const ConditionalComponent = ({ show }) => (
    <View>
      {show && <Text testID="conditional">Conditional text</Text>}
    </View>
  );

  const { rerender } = render(<ConditionalComponent show={false} />);

  // Element not present when show=false
  expect(screen.queryByTestId("conditional")).not.toBeOnTheScreen();

  rerender(<ConditionalComponent show={true} />);

  // Element present when show=true
  expect(screen.getByTestId("conditional")).toBeOnTheScreen();
  expect(screen.getByTestId("conditional")).toBeVisible();
});

State and Interaction Matchers

Matchers for testing interactive element states and accessibility properties.

/**
 * Assert element is disabled/enabled
 */
expect(element).toBeDisabled(): void;
expect(element).not.toBeDisabled(): void;
expect(element).toBeEnabled(): void;
expect(element).not.toBeEnabled(): void;

/**
 * Assert element is busy (has accessibilityState.busy = true)
 */
expect(element).toBeBusy(): void;
expect(element).not.toBeBusy(): void;

/**
 * Assert element is selected
 */
expect(element).toBeSelected(): void;
expect(element).not.toBeSelected(): void;

/**
 * Assert element is expanded/collapsed
 */
expect(element).toBeExpanded(): void;
expect(element).not.toBeExpanded(): void;
expect(element).toBeCollapsed(): void;
expect(element).not.toBeCollapsed(): void;

Usage Examples:

test("interactive state matchers", () => {
  render(
    <View>
      <Pressable 
        testID="disabled-button"
        disabled={true}
        accessibilityState={{ disabled: true }}
      >
        <Text>Disabled Button</Text>
      </Pressable>
      
      <Pressable
        testID="enabled-button"  
        disabled={false}
      >
        <Text>Enabled Button</Text>
      </Pressable>

      <View
        testID="busy-indicator"
        accessibilityState={{ busy: true }}
      >
        <Text>Loading...</Text>
      </View>

      <View
        testID="selected-item"
        accessibilityState={{ selected: true }}
      >
        <Text>Selected Item</Text>
      </View>

      <View
        testID="expanded-section"
        accessibilityState={{ expanded: true }}
      >
        <Text>Expanded Section</Text>
      </View>
    </View>
  );

  const disabledButton = screen.getByTestId("disabled-button");
  const enabledButton = screen.getByTestId("enabled-button");
  const busyIndicator = screen.getByTestId("busy-indicator");
  const selectedItem = screen.getByTestId("selected-item");
  const expandedSection = screen.getByTestId("expanded-section");

  // Disabled/enabled state
  expect(disabledButton).toBeDisabled();
  expect(enabledButton).toBeEnabled();
  expect(enabledButton).not.toBeDisabled();

  // Busy state
  expect(busyIndicator).toBeBusy();
  expect(enabledButton).not.toBeBusy();

  // Selected state
  expect(selectedItem).toBeSelected();
  expect(disabledButton).not.toBeSelected();

  // Expanded/collapsed state
  expect(expandedSection).toBeExpanded();
  expect(expandedSection).not.toBeCollapsed();
});

test("form element states", () => {
  render(
    <View>
      <TextInput
        testID="disabled-input"
        editable={false}
        accessibilityState={{ disabled: true }}
      />
      
      <Switch
        testID="toggle-switch"
        value={true}
        accessibilityState={{ checked: true }}
      />
    </View>
  );

  const disabledInput = screen.getByTestId("disabled-input");
  const toggleSwitch = screen.getByTestId("toggle-switch");

  expect(disabledInput).toBeDisabled();
  expect(toggleSwitch).toBeChecked();
});

Checkbox and Selection Matchers

Specialized matchers for checkbox-like components and selection states.

/**
 * Assert element is checked (for checkboxes, switches, radio buttons)
 */
expect(element).toBeChecked(): void;
expect(element).not.toBeChecked(): void;

/**
 * Assert element is partially checked (for indeterminate checkboxes)
 */
expect(element).toBePartiallyChecked(): void;
expect(element).not.toBePartiallyChecked(): void;

Usage Examples:

test("checkbox matchers", () => {
  const CheckboxComponent = ({ checked, indeterminate }) => (
    <Pressable
      testID="checkbox"
      accessibilityRole="checkbox"
      accessibilityState={{ 
        checked: indeterminate ? "mixed" : checked 
      }}
    >
      <Text>{indeterminate ? "[-]" : checked ? "[×]" : "[ ]"}</Text>
    </Pressable>
  );

  // Unchecked checkbox
  const { rerender } = render(
    <CheckboxComponent checked={false} indeterminate={false} />
  );
  
  const checkbox = screen.getByTestId("checkbox");
  expect(checkbox).not.toBeChecked();
  expect(checkbox).not.toBePartiallyChecked();

  // Checked checkbox
  rerender(<CheckboxComponent checked={true} indeterminate={false} />);
  expect(checkbox).toBeChecked();
  expect(checkbox).not.toBePartiallyChecked();

  // Indeterminate checkbox
  rerender(<CheckboxComponent checked={false} indeterminate={true} />);
  expect(checkbox).toBePartiallyChecked();
  expect(checkbox).not.toBeChecked();
});

test("switch component", () => {
  render(
    <Switch 
      testID="notification-switch"
      value={true}
      accessibilityLabel="Enable notifications"
      accessibilityState={{ checked: true }}
    />
  );

  const notificationSwitch = screen.getByTestId("notification-switch");
  expect(notificationSwitch).toBeChecked();
});

Content and Text Matchers

Matchers for testing text content and display values.

/**
 * Assert element has specific text content
 * @param text - Expected text content (string or RegExp)
 */
expect(element).toHaveTextContent(text: string | RegExp): void;
expect(element).not.toHaveTextContent(text: string | RegExp): void;

/**
 * Assert form element has specific display value
 * @param value - Expected display value (string or RegExp)
 */
expect(element).toHaveDisplayValue(value: string | RegExp): void;
expect(element).not.toHaveDisplayValue(value: string | RegExp): void;

Usage Examples:

test("text content matchers", () => {
  render(
    <View>
      <Text testID="greeting">Hello, World!</Text>
      <Text testID="user-name">John Doe</Text>
      <Text testID="empty-text"></Text>
      <View testID="container">
        <Text>First line</Text>
        <Text>Second line</Text>
      </View>
    </View>
  );

  const greeting = screen.getByTestId("greeting");
  const userName = screen.getByTestId("user-name");  
  const emptyText = screen.getByTestId("empty-text");
  const container = screen.getByTestId("container");

  // Exact text content
  expect(greeting).toHaveTextContent("Hello, World!");
  expect(userName).toHaveTextContent("John Doe");

  // RegExp matching
  expect(greeting).toHaveTextContent(/hello.*world/i);
  expect(userName).toHaveTextContent(/john/i);

  // Empty content
  expect(emptyText).toHaveTextContent("");
  expect(emptyText).not.toHaveTextContent("anything");

  // Container with multiple text elements
  expect(container).toHaveTextContent("First line Second line");
  expect(container).toHaveTextContent(/first.*second/i);
});

test("display value matchers", () => {
  render(
    <View>
      <TextInput
        testID="email-input"
        value="user@example.com"
        placeholder="Email"
      />
      
      <TextInput
        testID="empty-input"
        value=""
        placeholder="Empty input"
      />

      <Text testID="display-text">Current value: 42</Text>
    </View>
  );

  const emailInput = screen.getByTestId("email-input");
  const emptyInput = screen.getByTestId("empty-input");
  const displayText = screen.getByTestId("display-text");

  // Input values
  expect(emailInput).toHaveDisplayValue("user@example.com");
  expect(emailInput).toHaveDisplayValue(/@example\.com$/);
  expect(emptyInput).toHaveDisplayValue("");

  // Text element values
  expect(displayText).toHaveDisplayValue("Current value: 42");
  expect(displayText).toHaveDisplayValue(/value: \d+/);

  // Negative assertions
  expect(emailInput).not.toHaveDisplayValue("wrong@email.com");
  expect(emptyInput).not.toHaveDisplayValue("something");
});

Accessibility Matchers

Matchers for testing accessibility properties and labels.

/**
 * Assert element has specific accessible name
 * @param name - Expected accessible name (string or RegExp)
 */
expect(element).toHaveAccessibleName(name: string | RegExp): void;
expect(element).not.toHaveAccessibleName(name: string | RegExp): void;

/**
 * Assert element has specific accessibility value
 * @param value - Expected accessibility value object
 */
expect(element).toHaveAccessibilityValue(value: {
  min?: number;
  max?: number;
  now?: number;
  text?: string;
}): void;
expect(element).not.toHaveAccessibilityValue(value: object): void;

Usage Examples:

test("accessibility matchers", () => {
  render(
    <View>
      <Pressable
        testID="submit-button"
        accessibilityLabel="Submit form"
        accessibilityHint="Tap to submit the form"
      >
        <Text>Submit</Text>
      </Pressable>

      <Slider
        testID="volume-slider"
        minimumValue={0}
        maximumValue={100}
        value={75}
        accessibilityLabel="Volume control"
        accessibilityValue={{
          min: 0,
          max: 100,
          now: 75,
          text: "75 percent"
        }}
      />

      <TextInput
        testID="name-input"
        accessibilityLabel="Full name"
        placeholder="Enter your full name"
      />
    </View>
  );

  const submitButton = screen.getByTestId("submit-button");
  const volumeSlider = screen.getByTestId("volume-slider");
  const nameInput = screen.getByTestId("name-input");

  // Accessible names
  expect(submitButton).toHaveAccessibleName("Submit form");
  expect(submitButton).toHaveAccessibleName(/submit/i);
  expect(volumeSlider).toHaveAccessibleName("Volume control");
  expect(nameInput).toHaveAccessibleName("Full name");

  // Accessibility values
  expect(volumeSlider).toHaveAccessibilityValue({
    min: 0,
    max: 100,
    now: 75,
    text: "75 percent"
  });

  // Partial accessibility value matching
  expect(volumeSlider).toHaveAccessibilityValue({ now: 75 });
  expect(volumeSlider).toHaveAccessibilityValue({ text: "75 percent" });

  // Negative assertions
  expect(submitButton).not.toHaveAccessibleName("Cancel");
  expect(volumeSlider).not.toHaveAccessibilityValue({ now: 50 });
});

test("accessibility value updates", () => {
  const ProgressComponent = ({ progress }) => (
    <View
      testID="progress-bar"
      accessibilityRole="progressbar"
      accessibilityValue={{
        min: 0,
        max: 100,
        now: progress,
        text: `${progress}% complete`
      }}
    />
  );

  const { rerender } = render(<ProgressComponent progress={25} />);
  
  const progressBar = screen.getByTestId("progress-bar");

  // Initial progress
  expect(progressBar).toHaveAccessibilityValue({ 
    now: 25, 
    text: "25% complete" 
  });

  // Updated progress
  rerender(<ProgressComponent progress={75} />);
  expect(progressBar).toHaveAccessibilityValue({ 
    now: 75, 
    text: "75% complete" 
  });
});

Property and Style Matchers

Matchers for testing component props and styles.

/**
 * Assert element has specific prop with optional value
 * @param prop - Prop name to check
 * @param value - Optional expected prop value
 */
expect(element).toHaveProp(prop: string, value?: any): void;
expect(element).not.toHaveProp(prop: string, value?: any): void;

/**
 * Assert element has specific styles
 * @param styles - Expected style object
 */
expect(element).toHaveStyle(styles: object): void;
expect(element).not.toHaveStyle(styles: object): void;

Usage Examples:

test("prop matchers", () => {
  render(
    <View>
      <TextInput
        testID="styled-input"
        placeholder="Enter text"
        multiline={true}
        numberOfLines={4}
        keyboardType="email-address"
        autoCapitalize="none"
      />
      
      <Pressable
        testID="custom-button"
        disabled={false}
        onPress={() => {}}
        customProp="custom-value"
      >
        <Text>Button</Text>
      </Pressable>
    </View>
  );

  const styledInput = screen.getByTestId("styled-input");
  const customButton = screen.getByTestId("custom-button");

  // Check prop existence
  expect(styledInput).toHaveProp("placeholder");
  expect(styledInput).toHaveProp("multiline");
  expect(customButton).toHaveProp("onPress");

  // Check prop values
  expect(styledInput).toHaveProp("placeholder", "Enter text");
  expect(styledInput).toHaveProp("multiline", true);
  expect(styledInput).toHaveProp("numberOfLines", 4);
  expect(styledInput).toHaveProp("keyboardType", "email-address");
  expect(customButton).toHaveProp("disabled", false);
  expect(customButton).toHaveProp("customProp", "custom-value");

  // Negative assertions
  expect(styledInput).not.toHaveProp("nonexistent");
  expect(styledInput).not.toHaveProp("multiline", false);
  expect(customButton).not.toHaveProp("disabled", true);
});

test("style matchers", () => {
  render(
    <View>
      <View
        testID="styled-view"
        style={{
          backgroundColor: "blue",
          padding: 10,
          margin: 5,
          borderRadius: 8,
          flexDirection: "row"
        }}
      />
      
      <Text
        testID="styled-text"
        style={[
          { fontSize: 16, color: "red" },
          { fontWeight: "bold" }
        ]}
      >
        Styled Text
      </Text>
    </View>
  );

  const styledView = screen.getByTestId("styled-view");
  const styledText = screen.getByTestId("styled-text");

  // Single style properties
  expect(styledView).toHaveStyle({ backgroundColor: "blue" });
  expect(styledView).toHaveStyle({ padding: 10 });
  expect(styledText).toHaveStyle({ fontSize: 16 });
  expect(styledText).toHaveStyle({ color: "red" });

  // Multiple style properties
  expect(styledView).toHaveStyle({
    backgroundColor: "blue",
    padding: 10,
    margin: 5
  });

  expect(styledText).toHaveStyle({
    fontSize: 16,
    color: "red",
    fontWeight: "bold"
  });

  // Negative assertions
  expect(styledView).not.toHaveStyle({ backgroundColor: "red" });
  expect(styledView).not.toHaveStyle({ padding: 20 });
  expect(styledText).not.toHaveStyle({ fontSize: 20 });
});

Element Relationship Matchers

Matchers for testing element containment and hierarchy.

/**
 * Assert element contains another element
 * @param element - Element that should be contained
 */
expect(container).toContainElement(element: ReactTestInstance): void;
expect(container).not.toContainElement(element: ReactTestInstance): void;

Usage Examples:

test("containment matchers", () => {
  render(
    <View testID="outer-container">
      <View testID="inner-container">
        <Text testID="nested-text">Nested content</Text>
        <Pressable testID="nested-button">
          <Text>Button</Text>
        </Pressable>
      </View>
      <Text testID="sibling-text">Sibling content</Text>
    </View>
  );

  const outerContainer = screen.getByTestId("outer-container");
  const innerContainer = screen.getByTestId("inner-container");
  const nestedText = screen.getByTestId("nested-text");
  const nestedButton = screen.getByTestId("nested-button");
  const siblingText = screen.getByTestId("sibling-text");

  // Direct containment
  expect(outerContainer).toContainElement(innerContainer);
  expect(outerContainer).toContainElement(siblingText);
  expect(innerContainer).toContainElement(nestedText);
  expect(innerContainer).toContainElement(nestedButton);

  // Nested containment (transitive)
  expect(outerContainer).toContainElement(nestedText);
  expect(outerContainer).toContainElement(nestedButton);

  // Negative containment
  expect(innerContainer).not.toContainElement(siblingText);
  expect(nestedText).not.toContainElement(nestedButton);
  expect(siblingText).not.toContainElement(nestedText);
});

test("dynamic containment", () => {
  const DynamicContainer = ({ showNested }) => (
    <View testID="dynamic-container">
      <Text testID="always-present">Always here</Text>
      {showNested && (
        <View testID="conditional-nested">
          <Text testID="nested-content">Conditional content</Text>
        </View>
      )}
    </View>
  );

  const { rerender } = render(<DynamicContainer showNested={false} />);
  
  const container = screen.getByTestId("dynamic-container");
  const alwaysPresent = screen.getByTestId("always-present");

  // Always present element
  expect(container).toContainElement(alwaysPresent);

  // Conditional element not present initially
  expect(screen.queryByTestId("conditional-nested")).not.toBeOnTheScreen();

  // Show nested content
  rerender(<DynamicContainer showNested={true} />);
  
  const conditionalNested = screen.getByTestId("conditional-nested");
  const nestedContent = screen.getByTestId("nested-content");

  // Conditional elements now present and contained
  expect(container).toContainElement(conditionalNested);
  expect(container).toContainElement(nestedContent);
  expect(conditionalNested).toContainElement(nestedContent);
});

Custom Matcher Configuration

While the library provides comprehensive built-in matchers, you can also extend functionality if needed.

/**
 * Jest matcher result type for custom matchers
 */
interface MatcherResult {
  pass: boolean;
  message: () => string;
}

/**
 * Custom matcher function signature
 */
type CustomMatcher<T = ReactTestInstance> = (
  received: T,
  ...args: any[]
) => MatcherResult;

Usage Examples:

// Custom matcher example (typically in test setup file)
expect.extend({
  toHaveCustomProperty(received, expectedValue) {
    const pass = received.props.customProperty === expectedValue;
    
    return {
      pass,
      message: () => 
        pass
          ? `Expected element not to have customProperty: ${expectedValue}`
          : `Expected element to have customProperty: ${expectedValue}, but got: ${received.props.customProperty}`
    };
  }
});

// Usage in tests
test("custom matcher", () => {
  render(
    <View testID="custom-element" customProperty="special-value">
      <Text>Custom Element</Text>
    </View>
  );

  const element = screen.getByTestId("custom-element");
  expect(element).toHaveCustomProperty("special-value");
});

Install with Tessl CLI

npx tessl i tessl/npm-testing-library--react-native

docs

async-testing.md

configuration.md

hooks.md

index.md

interactions.md

matchers.md

queries.md

rendering.md

tile.json