React Component Testing Framework
Building robust and reliable React applications requires thorough component testing. This challenge asks you to create a simplified, yet functional, component testing framework in React using TypeScript. This framework will allow you to easily render React components, interact with them (e.g., click buttons, type into inputs), and assert their state and rendered output.
Problem Description
You are tasked with building a basic component testing framework. The framework should provide functions to:
- Render a React component: Given a React component and its props, render it into a detached DOM node.
- Interact with the component: Provide functions to simulate user interactions like clicking elements, typing into input fields, and selecting options from dropdowns. These functions should accept a selector (e.g., CSS selector) to identify the element to interact with.
- Assert the component's state and output: Provide functions to assert that the rendered component's output matches an expected value or that a specific element exists within the rendered DOM. Assertions should include checking for the presence of elements, their text content, and potentially their attributes.
- Unmount the component: Clean up the detached DOM node after the tests are complete.
Key Requirements:
- The framework should be written in TypeScript.
- It should be relatively lightweight and easy to use.
- It should not rely on external testing libraries like Jest or React Testing Library (the goal is to build a basic framework, not integrate with an existing one).
- The framework should handle basic React components with props.
- The framework should provide a clear and concise API.
Expected Behavior:
The framework should allow you to write tests that verify the behavior of your React components in isolation. Tests should be able to simulate user interactions and assert that the component responds correctly.
Edge Cases to Consider:
- Components that render dynamically.
- Components that use context.
- Components that use hooks.
- Asynchronous updates to the component's state. (While full asynchronous handling isn't required, consider how your framework might be extended to support it.)
- Error handling (e.g., what happens if a selector doesn't match any element?).
Examples
Example 1:
Input:
Component: SimpleButton.tsx (renders a button with text "Click Me")
Props: {}
Test:
- Render the component
- Click the button
- Assert that the button's text changes to "Clicked!"
Output:
Test passes.
Explanation: The framework renders the button, simulates a click, and verifies that the button's text content is updated as expected.
Example 2:
Input:
Component: InputComponent.tsx (renders an input field with a label)
Props: { label: "Enter your name" }
Test:
- Render the component
- Type "John Doe" into the input field
- Assert that the input field's value is "John Doe"
Output:
Test passes.
Explanation: The framework renders the input field, simulates typing text into it, and verifies that the input field's value is correctly updated.
Example 3:
Input:
Component: ConditionalDisplay.tsx (renders a paragraph if a prop 'isVisible' is true)
Props: { isVisible: false }
Test:
- Render the component
- Assert that the paragraph element is not present in the DOM.
Output:
Test passes.
Explanation: The framework renders the component with `isVisible` set to false, and verifies that the paragraph element is not rendered.
Constraints
- DOM Manipulation: You are allowed to manipulate the DOM to render and interact with components. However, avoid directly modifying the main document. Use a detached DOM node.
- Performance: While not a primary focus, avoid excessively inefficient DOM manipulations.
- Component Complexity: The framework should handle relatively simple components. Complex components with intricate logic or third-party dependencies are outside the scope of this challenge.
- No External Testing Libraries: Do not use Jest, React Testing Library, or similar testing frameworks. The goal is to build a basic framework from scratch.
- TypeScript: The entire solution must be written in TypeScript.
Notes
- Consider using
document.createElement('div')to create a detached DOM node for rendering components. - You can use
document.querySelectoror similar methods to find elements within the rendered DOM. - Think about how to structure your framework to be extensible and maintainable.
- Focus on providing a clear and concise API for rendering, interacting with, and asserting on components.
- Error handling is important. Consider what should happen if a selector doesn't match any element or if an assertion fails. Returning boolean success/failure is acceptable.
- This is a simplified framework. Real-world testing frameworks are much more complex and feature-rich. The goal here is to understand the fundamental concepts involved in component testing.