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

configuration.mddocs/

Configuration and Setup

Global configuration options and setup utilities for customizing React Native Testing Library behavior across your test suite, including async timeouts, debugging options, and rendering modes.

Capabilities

Global Configuration

Configure library-wide settings that affect all tests in your suite.

/**
 * Configure global options for React Native Testing Library
 * @param options - Configuration options to apply
 */
function configure(options: Partial<Config & ConfigAliasOptions>): void;

/**
 * Reset all configuration to default values
 */
function resetToDefaults(): void;

/**
 * Get current configuration settings
 * @returns Current configuration object
 */
function getConfig(): Config;

interface Config {
  /** Default timeout in milliseconds for waitFor and findBy queries */
  asyncUtilTimeout: number;
  
  /** Default value for includeHiddenElements query option */
  defaultIncludeHiddenElements: boolean;
  
  /** Default options for debug helper function */
  defaultDebugOptions?: Partial<DebugOptions>;
  
  /** Enable/disable concurrent rendering by default */
  concurrentRoot: boolean;
}

interface ConfigAliasOptions {
  /** Alias for defaultIncludeHiddenElements (RTL compatibility) */
  defaultHidden: boolean;
}

interface DebugOptions {
  /** Function to transform props before displaying */
  mapProps?: (props: Record<string, any>) => Record<string, any>;
  
  /** Maximum length of debug output */
  maxLength?: number;
}

Usage Examples:

import { configure, resetToDefaults, getConfig } from "@testing-library/react-native";

describe("configuration examples", () => {
  afterEach(() => {
    // Reset to defaults after each test
    resetToDefaults();
  });

  test("async timeout configuration", async () => {
    // Set longer timeout for slow components
    configure({ asyncUtilTimeout: 5000 });

    const SlowComponent = () => {
      const [loaded, setLoaded] = useState(false);
      
      useEffect(() => {
        setTimeout(() => setLoaded(true), 3000);
      }, []);

      return loaded ? <Text>Loaded</Text> : <Text>Loading...</Text>;
    };

    render(<SlowComponent />);

    // This will use the configured 5 second timeout
    await screen.findByText("Loaded");
    
    // Verify configuration was applied
    expect(getConfig().asyncUtilTimeout).toBe(5000);
  });

  test("hidden elements configuration", () => {
    // Include hidden elements by default
    configure({ defaultIncludeHiddenElements: true });

    render(
      <View>
        <Text>Visible text</Text>
        <Text style={{ opacity: 0 }}>Hidden text</Text>
        <View accessibilityElementsHidden>
          <Text>Accessibility hidden</Text>
        </View>
      </View>
    );

    // These queries will now include hidden elements by default
    const allTexts = screen.getAllByText(/text/);
    expect(allTexts.length).toBeGreaterThan(1);

    // Can still override per query
    const visibleOnly = screen.getAllByText(/text/, { 
      includeHiddenElements: false 
    });
    expect(visibleOnly.length).toBeLessThan(allTexts.length);
  });

  test("debug options configuration", () => {
    // Configure debug output
    configure({
      defaultDebugOptions: {
        maxLength: 500,
        mapProps: (props) => ({
          ...props,
          // Hide functions in debug output
          onPress: props.onPress ? "[Function]" : undefined,
          style: "[Style Object]"
        })
      }
    });

    const MyComponent = () => (
      <Pressable 
        onPress={() => {}}
        style={{ backgroundColor: "blue" }}
      >
        <Text>Debug me</Text>
      </Pressable>
    );

    const { debug } = render(<MyComponent />);
    
    // Debug will use configured options
    debug(); // Output will show "[Function]" instead of actual function
  });

  test("concurrent rendering configuration", () => {
    // Disable concurrent rendering globally
    configure({ concurrentRoot: false });

    const AsyncComponent = () => {
      const [value, setValue] = useState("initial");
      
      useEffect(() => {
        setValue("updated");
      }, []);

      return <Text>{value}</Text>;
    };

    // This will use legacy rendering mode
    render(<AsyncComponent />);
    
    expect(screen.getByText("updated")).toBeOnTheScreen();
    expect(getConfig().concurrentRoot).toBe(false);
  });

  test("RTL compatibility aliases", () => {
    // Using RTL-style alias
    configure({ defaultHidden: true });

    // Should set defaultIncludeHiddenElements
    expect(getConfig().defaultIncludeHiddenElements).toBe(true);
  });
});

Test Environment Setup

Configuration patterns for different test environments and frameworks.

/**
 * Environment-specific setup utilities
 */

Usage Examples:

// jest.config.js or test setup file
import { configure } from "@testing-library/react-native";

// Global test suite configuration
configure({
  // Longer timeout for integration tests
  asyncUtilTimeout: 2000,
  
  // Include hidden elements for comprehensive testing
  defaultIncludeHiddenElements: false,
  
  // Enable concurrent rendering for modern React features
  concurrentRoot: true,
  
  // Clean debug output
  defaultDebugOptions: {
    maxLength: 1000,
    mapProps: (props) => {
      const cleaned = { ...props };
      
      // Hide complex objects for cleaner output
      Object.keys(cleaned).forEach(key => {
        if (typeof cleaned[key] === "function") {
          cleaned[key] = "[Function]";
        } else if (key === "style" && typeof cleaned[key] === "object") {
          cleaned[key] = "[Style]";
        }
      });
      
      return cleaned;
    }
  }
});

// Project-specific setup
test("project configuration", () => {
  // Configuration for React Native Paper components
  configure({
    defaultDebugOptions: {
      mapProps: (props) => ({
        ...props,
        // Paper-specific props cleanup
        theme: props.theme ? "[Theme]" : undefined,
        contentStyle: props.contentStyle ? "[Style]" : undefined
      })
    }
  });

  const PaperButton = () => (
    <Button mode="contained" onPress={() => {}}>
      Paper Button
    </Button>
  );

  const { debug } = render(<PaperButton />);
  debug(); // Clean output without theme object details
});

Auto-Cleanup Configuration

Configuration for automatic test cleanup behavior.

/**
 * Auto-cleanup configuration via environment variables and imports
 */

// Standard import with auto-cleanup
import { render } from "@testing-library/react-native";

// Pure import without auto-cleanup  
import { render } from "@testing-library/react-native/pure";

// Environment variable to disable auto-cleanup
// Set RNTL_SKIP_AUTO_CLEANUP=true to disable automatic cleanup

Usage Examples:

// Standard usage with auto-cleanup (default)
describe("auto-cleanup enabled", () => {
  test("first test", () => {
    render(<Text>First test</Text>);
    expect(screen.getByText("First test")).toBeOnTheScreen();
    // Automatic cleanup happens after this test
  });

  test("second test", () => {
    // Screen is clean from previous test
    render(<Text>Second test</Text>);
    expect(screen.getByText("Second test")).toBeOnTheScreen();
    expect(screen.queryByText("First test")).not.toBeOnTheScreen();
  });
});

// Manual cleanup control
describe("manual cleanup control", () => {
  // Import pure version
  const { render, cleanup } = require("@testing-library/react-native/pure");

  afterEach(() => {
    // Manual cleanup after each test
    cleanup();
  });

  test("controlled cleanup", () => {
    render(<Text>Controlled test</Text>);
    expect(screen.getByText("Controlled test")).toBeOnTheScreen();
    
    // Could do additional cleanup here if needed
    cleanup();
    
    // Screen is now clean
    expect(screen.queryByText("Controlled test")).not.toBeOnTheScreen();
  });
});

// Environment variable configuration
describe("environment-based cleanup", () => {
  const originalEnv = process.env.RNTL_SKIP_AUTO_CLEANUP;

  afterAll(() => {
    // Restore original environment
    if (originalEnv !== undefined) {
      process.env.RNTL_SKIP_AUTO_CLEANUP = originalEnv;
    } else {
      delete process.env.RNTL_SKIP_AUTO_CLEANUP;
    }
  });

  test("disable auto-cleanup via environment", () => {
    // This would typically be set in test environment configuration
    process.env.RNTL_SKIP_AUTO_CLEANUP = "true";
    
    // Re-import would be needed to pick up env change
    // In practice, this is set before importing the library
    
    render(<Text>No auto cleanup</Text>);
    // Manual cleanup required when env var is set
  });
});

Debug Configuration

Advanced debugging configuration for test development and troubleshooting.

/**
 * Debug configuration options
 */
interface DebugOptions {
  /** Transform props before displaying */
  mapProps?: (props: Record<string, any>) => Record<string, any>;
  
  /** Maximum length of debug output */
  maxLength?: number;
}

/**
 * Debug function type
 */
interface DebugFunction {
  (element?: ReactTestInstance, maxLength?: number, options?: DebugOptions): void;
}

Usage Examples:

test("debug configuration examples", () => {
  // Component with complex props
  const ComplexComponent = () => (
    <View>
      <Pressable
        onPress={() => console.log("pressed")}
        style={{
          backgroundColor: "blue",
          padding: 10,
          borderRadius: 5
        }}
        testID="complex-button"
      >
        <Text>Complex Button</Text>
      </Pressable>
      <FlatList
        data={[1, 2, 3]}
        renderItem={({ item }) => <Text key={item}>Item {item}</Text>}
        keyExtractor={(item) => item.toString()}
      />
    </View>
  );

  const { debug } = render(<ComplexComponent />);

  // Default debug output (might be verbose)
  debug();

  // Limited length debug
  debug(undefined, 200);

  // Custom props mapping
  debug(undefined, undefined, {
    mapProps: (props) => ({
      ...props,
      // Simplify complex props
      onPress: props.onPress ? "[Function]" : undefined,
      style: props.style ? "[Style Object]" : undefined,
      data: props.data ? `[Array of ${props.data.length}]` : undefined,
      renderItem: props.renderItem ? "[Render Function]" : undefined
    })
  });

  // Debug specific element
  const button = screen.getByTestId("complex-button");
  debug(button, 500, {
    mapProps: (props) => ({
      testID: props.testID,
      onPress: "[Function]",
      style: "[Simplified]"
    })
  });
});

test("debug configuration per test suite", () => {
  // Suite-specific debug configuration
  const originalConfig = getConfig();
  
  configure({
    defaultDebugOptions: {
      maxLength: 300,
      mapProps: (props) => {
        // Project-specific prop cleaning
        return Object.keys(props).reduce((clean, key) => {
          if (key.startsWith("accessibility")) {
            clean[key] = props[key];
          } else if (typeof props[key] === "function") {
            clean[key] = `[${key}Function]`;
          } else if (key === "children" && Array.isArray(props[key])) {
            clean[key] = `[${props[key].length} children]`;
          } else {
            clean[key] = props[key];
          }
          return clean;
        }, {});
      }
    }
  });

  const TestComponent = () => (
    <View accessibilityLabel="Test container">
      <Text>Child 1</Text>
      <Text>Child 2</Text>
      <Pressable onPress={() => {}} accessibilityRole="button">
        <Text>Button</Text>
      </Pressable>
    </View>
  );

  const { debug } = render(<TestComponent />);
  
  // Uses configured debug options
  debug();

  // Restore original config
  configure(originalConfig);
});

Performance Configuration

Configuration options for optimizing test performance.

/**
 * Performance-related configuration
 */

Usage Examples:

describe("performance configuration", () => {
  test("optimized for fast tests", () => {
    configure({
      // Shorter timeout for unit tests
      asyncUtilTimeout: 500,
      
      // Disable concurrent rendering for predictable timing
      concurrentRoot: false,
      
      // Minimal debug output
      defaultDebugOptions: {
        maxLength: 100,
        mapProps: (props) => ({ testID: props.testID })
      }
    });

    const FastComponent = () => {
      const [value, setValue] = useState("initial");
      
      useEffect(() => {
        // Fast update
        setTimeout(() => setValue("updated"), 10);
      }, []);

      return <Text testID="fast-text">{value}</Text>;
    };

    render(<FastComponent />);
    
    // Fast assertion with short timeout
    return screen.findByText("updated");
  });

  test("optimized for integration tests", () => {
    configure({
      // Longer timeout for complex interactions
      asyncUtilTimeout: 10000,
      
      // Enable concurrent rendering for realistic testing
      concurrentRoot: true,
      
      // Include hidden elements for comprehensive testing
      defaultIncludeHiddenElements: true,
      
      // Detailed debug output for troubleshooting
      defaultDebugOptions: {
        maxLength: 2000
      }
    });

    const IntegrationComponent = () => {
      const [loading, setLoading] = useState(true);
      const [data, setData] = useState(null);

      useEffect(() => {
        // Simulate slow API call
        setTimeout(() => {
          setData("Integration data");
          setLoading(false);
        }, 1000);
      }, []);

      return loading ? (
        <Text>Loading integration test...</Text>
      ) : (
        <Text testID="integration-result">{data}</Text>
      );
    };

    render(<IntegrationComponent />);
    
    // Long timeout for integration test
    return screen.findByTestId("integration-result");
  });

  test("memory-conscious configuration", () => {
    configure({
      // Compact debug output to reduce memory usage
      defaultDebugOptions: {
        maxLength: 50,
        mapProps: () => ({ "[props]": "..." }) // Minimal prop display
      }
    });

    // Large component tree
    const LargeComponent = () => (
      <View>
        {Array.from({ length: 100 }, (_, i) => (
          <Text key={i} testID={`item-${i}`}>
            Item {i}
          </Text>
        ))}
      </View>
    );

    const { debug } = render(<LargeComponent />);
    
    // Compact debug output
    debug();
    
    // Test still works normally
    expect(screen.getByTestId("item-0")).toBeOnTheScreen();
    expect(screen.getByTestId("item-99")).toBeOnTheScreen();
  });
});

Environment Detection and Adaptation

Configuration that adapts to different testing environments.

/**
 * Environment-aware configuration
 */

Usage Examples:

// Test environment detection and configuration
describe("environment-aware configuration", () => {
  test("CI environment configuration", () => {
    const isCI = process.env.CI === "true";
    
    configure({
      // Longer timeouts in CI environments
      asyncUtilTimeout: isCI ? 10000 : 2000,
      
      // Disable concurrent rendering in CI for stability
      concurrentRoot: !isCI,
      
      // More verbose debugging in CI
      defaultDebugOptions: {
        maxLength: isCI ? 5000 : 1000
      }
    });

    const EnvironmentComponent = () => {
      const [env, setEnv] = useState("unknown");
      
      useEffect(() => {
        // Simulate environment detection
        setTimeout(() => {
          setEnv(isCI ? "CI" : "local");
        }, isCI ? 500 : 100);
      }, []);

      return <Text testID="env-indicator">Environment: {env}</Text>;
    };

    render(<EnvironmentComponent />);
    
    // Timeout will be appropriate for environment
    return screen.findByText(/Environment: (CI|local)/);
  });

  test("jest environment configuration", () => {
    const isJest = typeof jest !== "undefined";
    
    if (isJest) {
      configure({
        defaultDebugOptions: {
          mapProps: (props) => ({
            ...props,
            // Jest-specific prop handling
            onPress: props.onPress ? "[Jest Mock Function]" : undefined
          })
        }
      });
    }

    const JestComponent = () => {
      const mockFn = isJest ? jest.fn() : () => {};
      
      return (
        <Pressable onPress={mockFn} testID="jest-button">
          <Text>Jest Button</Text>
        </Pressable>
      );
    };

    const { debug } = render(<JestComponent />);
    
    if (isJest) {
      debug(); // Will show "[Jest Mock Function]"
    }

    expect(screen.getByTestId("jest-button")).toBeOnTheScreen();
  });

  test("development vs production configuration", () => {
    const isDev = process.env.NODE_ENV !== "production";
    
    configure({
      defaultDebugOptions: {
        maxLength: isDev ? 2000 : 500,
        mapProps: isDev 
          ? undefined // Full props in development
          : (props) => ({ testID: props.testID }) // Minimal in production
      }
    });

    const DevComponent = () => (
      <View
        testID="dev-component"
        style={{ padding: 10 }}
        accessibilityLabel="Development component"
      >
        <Text>Development Component</Text>
      </View>
    );

    const { debug } = render(<DevComponent />);
    
    // Debug verbosity depends on environment
    debug();
    
    expect(screen.getByTestId("dev-component")).toBeOnTheScreen();
  });
});

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