Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@quilted/react-testing

Package Overview
Dependencies
Maintainers
1
Versions
161
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@quilted/react-testing - npm Package Compare versions

Comparing version 0.5.25 to 0.5.26

LICENSE.md

44

build/typescript/environment.d.ts

@@ -20,31 +20,31 @@ import type { ReactElement } from 'react';

}
export type AfterMountOption<MountOptions extends PlainObject, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> = Async extends true ? {
afterMount(wrapper: RootNode<unknown, Context, Actions, Extensions>, options: MountOptions): PromiseLike<void>;
export type AfterRenderOption<RenderOptions extends PlainObject, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> = Async extends true ? {
afterRender(wrapper: RootNode<unknown, Context, Actions, Extensions>, options: RenderOptions): PromiseLike<void>;
} : {
afterMount?(wrapper: RootNode<unknown, Context, Actions, Extensions>, options: MountOptions): void;
afterRender?(wrapper: RootNode<unknown, Context, Actions, Extensions>, options: RenderOptions): void;
};
export type ContextOption<MountOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject> = AdditionalContext extends EmptyObject ? {
export type ContextOption<RenderOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject> = AdditionalContext extends EmptyObject ? {
context?: never;
} : {
context(options: MountOptions, existingContext: ExistingContext): AdditionalContext;
context(options: RenderOptions, existingContext: ExistingContext): AdditionalContext;
};
export type ActionsOption<MountOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject> = AdditionalActions extends EmptyObject ? {
export type ActionsOption<RenderOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject> = AdditionalActions extends EmptyObject ? {
actions?: never;
} : {
actions(root: Omit<RootNode<unknown, Context, any, Extensions>, 'actions'>, options: MountOptions, existingActions: ExistingActions): AdditionalActions;
actions(root: Omit<RootNode<unknown, Context, any, Extensions>, 'actions'>, options: RenderOptions, existingActions: ExistingActions): AdditionalActions;
};
export interface RenderOption<MountOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject> {
render?(element: ReactElement<any>, context: Context, options: MountOptions): ReactElement<any>;
export interface RenderOption<RenderOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject> {
render?(element: ReactElement<any>, context: Context, options: RenderOptions): ReactElement<any>;
}
export type MountOptionsOverrideOption<ExistingMountOptions extends PlainObject = EmptyObject, AdditionalMountOptions extends PlainObject = EmptyObject> = AdditionalMountOptions extends EmptyObject ? {
export type RenderOptionsOverrideOption<ExistingRenderOptions extends PlainObject = EmptyObject, AdditionalRenderOptions extends PlainObject = EmptyObject> = AdditionalRenderOptions extends EmptyObject ? {
options?: never;
} : {
options?(current: MergeObjects<ExistingMountOptions, AdditionalMountOptions>): Partial<AdditionalMountOptions>;
options?(current: MergeObjects<ExistingRenderOptions, AdditionalRenderOptions>): Partial<AdditionalRenderOptions>;
};
export type CustomMountOptions<MountOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject, Async extends boolean = false> = RenderOption<MountOptions, MergeObjects<ExistingContext, AdditionalContext>> & ContextOption<MountOptions, ExistingContext, AdditionalContext> & ActionsOption<MountOptions, MergeObjects<ExistingContext, AdditionalContext>, ExistingActions, AdditionalActions, Extensions> & AfterMountOption<MountOptions, MergeObjects<ExistingContext, AdditionalContext>, MergeObjects<ExistingActions, AdditionalActions>, Extensions, Async>;
export type CustomMountExtendOptions<ExistingMountOptions extends PlainObject = EmptyObject, AdditionalMountOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject, Async extends boolean = false> = CustomMountOptions<MergeObjects<ExistingMountOptions, AdditionalMountOptions>, ExistingContext, AdditionalContext, ExistingActions, AdditionalActions, Extensions, Async> & MountOptionsOverrideOption<ExistingMountOptions, AdditionalMountOptions>;
export interface CustomMount<MountOptions extends PlainObject, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> {
<Props>(element: ReactElement<any>, options?: MountOptions): CustomMountResult<Props, Context, Actions, Extensions, Async>;
extend<AdditionalMountOptions extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, AdditionalAsync extends boolean = false>(options: CustomMountExtendOptions<MountOptions, AdditionalMountOptions, Context, AdditionalContext, Actions, AdditionalActions, Extensions, AdditionalAsync>): CustomMount<MergeObjects<MountOptions, AdditionalMountOptions>, MergeObjects<Context, AdditionalContext>, MergeObjects<Actions, AdditionalActions>, Extensions, AdditionalAsync extends true ? AdditionalAsync : Async>;
hook<T>(useHook: () => T, options?: MountOptions): Async extends true ? Promise<HookRunner<T, Context, Actions>> : HookRunner<T, Context, Actions>;
export type CustomRenderOptions<RenderOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject, Async extends boolean = false> = RenderOption<RenderOptions, MergeObjects<ExistingContext, AdditionalContext>> & ContextOption<RenderOptions, ExistingContext, AdditionalContext> & ActionsOption<RenderOptions, MergeObjects<ExistingContext, AdditionalContext>, ExistingActions, AdditionalActions, Extensions> & AfterRenderOption<RenderOptions, MergeObjects<ExistingContext, AdditionalContext>, MergeObjects<ExistingActions, AdditionalActions>, Extensions, Async>;
export type CustomRenderExtendOptions<ExistingRenderOptions extends PlainObject = EmptyObject, AdditionalRenderOptions extends PlainObject = EmptyObject, ExistingContext extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, ExistingActions extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, Extensions extends PlainObject = EmptyObject, Async extends boolean = false> = CustomRenderOptions<MergeObjects<ExistingRenderOptions, AdditionalRenderOptions>, ExistingContext, AdditionalContext, ExistingActions, AdditionalActions, Extensions, Async> & RenderOptionsOverrideOption<ExistingRenderOptions, AdditionalRenderOptions>;
export interface CustomRender<RenderOptions extends PlainObject, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> {
<Props>(element: ReactElement<any>, options?: RenderOptions): CustomRenderResult<Props, Context, Actions, Extensions, Async>;
extend<AdditionalRenderOptions extends PlainObject = EmptyObject, AdditionalContext extends PlainObject = EmptyObject, AdditionalActions extends PlainObject = EmptyObject, AdditionalAsync extends boolean = false>(options: CustomRenderExtendOptions<RenderOptions, AdditionalRenderOptions, Context, AdditionalContext, Actions, AdditionalActions, Extensions, AdditionalAsync>): CustomRender<MergeObjects<RenderOptions, AdditionalRenderOptions>, MergeObjects<Context, AdditionalContext>, MergeObjects<Actions, AdditionalActions>, Extensions, AdditionalAsync extends true ? AdditionalAsync : Async>;
hook<T>(useHook: () => T, options?: RenderOptions): Async extends true ? Promise<HookRunner<T, Context, Actions>> : HookRunner<T, Context, Actions>;
}

@@ -60,8 +60,8 @@ export interface HookRunner<HookReturn, Context extends PlainObject, Actions extends PlainObject> {

}
export type CustomMountResult<Props, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> = Async extends true ? Promise<RootNode<Props, Context, Actions, Extensions>> : RootNode<Props, Context, Actions, Extensions>;
export type CustomRenderResult<Props, Context extends PlainObject, Actions extends PlainObject, Extensions extends PlainObject, Async extends boolean> = Async extends true ? Promise<RootNode<Props, Context, Actions, Extensions>> : RootNode<Props, Context, Actions, Extensions>;
export interface Environment<Extensions extends PlainObject = EmptyObject> {
readonly mounted: Set<RootNode<any, any, any, Extensions>>;
readonly mount: CustomMount<EmptyObject, EmptyObject, EmptyObject, Extensions, false>;
createMount<MountOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject, Actions extends PlainObject = EmptyObject, Async extends boolean = false>(options: CustomMountOptions<MountOptions, Context, Context, Actions, Actions, Extensions, Async>): CustomMount<MountOptions, Context, Actions, Extensions, Async>;
unmountAll(): void;
readonly rendered: Set<RootNode<any, any, any, Extensions>>;
readonly render: CustomRender<EmptyObject, EmptyObject, EmptyObject, Extensions, false>;
createRender<RenderOptions extends PlainObject = EmptyObject, Context extends PlainObject = EmptyObject, Actions extends PlainObject = EmptyObject, Async extends boolean = false>(options: CustomRenderOptions<RenderOptions, Context, Context, Actions, Actions, Extensions, Async>): CustomRender<RenderOptions, Context, Actions, Extensions, Async>;
destroyAll(): void;
}

@@ -68,0 +68,0 @@ export declare function createEnvironment<EnvironmentContext = undefined, Extensions extends PlainObject = EmptyObject>(env: EnvironmentOptions<EnvironmentContext, Extensions>): Environment<Extensions>;

import type { VNode, Component } from 'preact';
import { isNode } from '../environment';
import type { CustomMount } from '../environment';
import type { CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption } from '../environment';
import type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions } from '../types';
export { isNode };
export type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomMount };
declare const mount: CustomMount<import("../types").EmptyObject, import("../types").EmptyObject, import("../types").EmptyObject, HtmlNodeExtensions, false>, createMount: <MountOptions extends import("../types").PlainObject = import("../types").EmptyObject, Context_1 extends import("../types").PlainObject = import("../types").EmptyObject, Actions extends import("../types").PlainObject = import("../types").EmptyObject, Async extends boolean = false>(options: import("../environment").CustomMountOptions<MountOptions, Context_1, Context_1, Actions, Actions, HtmlNodeExtensions, Async>) => CustomMount<MountOptions, Context_1, Actions, HtmlNodeExtensions, Async>, mounted: Set<Root<any, any, any, HtmlNodeExtensions>>, unmountAll: () => void;
export { mount, createMount, mounted, unmountAll };
export type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption, };
declare const render: CustomRender<import("../types").EmptyObject, import("../types").EmptyObject, import("../types").EmptyObject, HtmlNodeExtensions, false>, createRender: <RenderOptions extends import("../types").PlainObject = import("../types").EmptyObject, Context_1 extends import("../types").PlainObject = import("../types").EmptyObject, Actions extends import("../types").PlainObject = import("../types").EmptyObject, Async extends boolean = false>(options: CustomRenderOptions<RenderOptions, Context_1, Context_1, Actions, Actions, HtmlNodeExtensions, Async>) => CustomRender<RenderOptions, Context_1, Actions, HtmlNodeExtensions, Async>, rendered: Set<Root<any, any, any, HtmlNodeExtensions>>, destroyAll: () => void;
export { render, createRender, rendered, destroyAll };
/**

@@ -10,0 +10,0 @@ * Preact mangles it's private internals, these types help us access them safely(ish)

import { isNode } from '../environment';
import type { CustomMount, EnvironmentOptions } from '../environment';
import type { CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption } from '../environment';
import type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions } from '../types';
export { isNode };
export type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomMount };
declare const mount: CustomMount<import("../types").EmptyObject, import("../types").EmptyObject, import("../types").EmptyObject, HtmlNodeExtensions, false>, createMount: <MountOptions extends import("../types").PlainObject = import("../types").EmptyObject, Context_1 extends import("../types").PlainObject = import("../types").EmptyObject, Actions extends import("../types").PlainObject = import("../types").EmptyObject, Async extends boolean = false>(options: import("../environment").CustomMountOptions<MountOptions, Context_1, Context_1, Actions, Actions, HtmlNodeExtensions, Async>) => CustomMount<MountOptions, Context_1, Actions, HtmlNodeExtensions, Async>, mounted: Set<Root<any, any, any, HtmlNodeExtensions>>, unmountAll: () => void;
export { mount, createMount, mounted, unmountAll };
export type { Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption, };
declare const render: CustomRender<import("../types").EmptyObject, import("../types").EmptyObject, import("../types").EmptyObject, HtmlNodeExtensions, false>, createRender: <RenderOptions extends import("../types").PlainObject = import("../types").EmptyObject, Context_1 extends import("../types").PlainObject = import("../types").EmptyObject, Actions extends import("../types").PlainObject = import("../types").EmptyObject, Async extends boolean = false>(options: CustomRenderOptions<RenderOptions, Context_1, Context_1, Actions, Actions, HtmlNodeExtensions, Async>) => CustomRender<RenderOptions, Context_1, Actions, HtmlNodeExtensions, Async>, rendered: Set<Root<any, any, any, HtmlNodeExtensions>>, destroyAll: () => void;
export { render, createRender, rendered, destroyAll };
type Create = Parameters<EnvironmentOptions<any, HtmlNodeExtensions>['update']>[1];

@@ -9,0 +9,0 @@ type Child = ReturnType<Create> | string;

@@ -1,3 +0,5 @@

declare const mount: import("../environment").CustomMount<import("..").EmptyObject, import("..").EmptyObject, import("..").EmptyObject, import("..").EmptyObject, false>, createMount: <MountOptions extends import("..").PlainObject = import("..").EmptyObject, Context_1 extends import("..").PlainObject = import("..").EmptyObject, Actions extends import("..").PlainObject = import("..").EmptyObject, Async extends boolean = false>(options: import("../environment").CustomMountOptions<MountOptions, Context_1, Context_1, Actions, Actions, import("..").EmptyObject, Async>) => import("../environment").CustomMount<MountOptions, Context_1, Actions, import("..").EmptyObject, Async>, mounted: Set<import("../types").Root<any, any, any, import("..").EmptyObject>>, unmountAll: () => void;
export { mount, createMount, mounted, unmountAll };
import type { CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption } from '../environment';
export type { CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption, };
declare const render: CustomRender<import("..").EmptyObject, import("..").EmptyObject, import("..").EmptyObject, import("..").EmptyObject, false>, createRender: <RenderOptions extends import("..").PlainObject = import("..").EmptyObject, Context_1 extends import("..").PlainObject = import("..").EmptyObject, Actions extends import("..").PlainObject = import("..").EmptyObject, Async extends boolean = false>(options: CustomRenderOptions<RenderOptions, Context_1, Context_1, Actions, Actions, import("..").EmptyObject, Async>) => CustomRender<RenderOptions, Context_1, Actions, import("..").EmptyObject, Async>, rendered: Set<import("../types").Root<any, any, any, import("..").EmptyObject>>, destroyAll: () => void;
export { render, createRender, rendered, destroyAll };
//# sourceMappingURL=test-renderer.d.ts.map
export * from './implementations/test-renderer';
export * from './types';
export { createEnvironment } from './environment';
export type { CustomMount, HookRunner, EnvironmentOptions, ContextOption, RenderOption, ActionsOption, } from './environment';
export type { CustomRender, CustomRenderResult, CustomRenderOptions, CustomRenderExtendOptions, HookRunner, Environment, EnvironmentOptions, ContextOption, RenderOption, ActionsOption, } from './environment';
//# sourceMappingURL=index.d.ts.map
# @quilted/react-testing
## 0.5.26
### Patch Changes
- [#518](https://github.com/lemonmade/quilt/pull/518) [`10574343`](https://github.com/lemonmade/quilt/commit/105743435ad7143acb50dfdee92f6d3422167888) Thanks [@lemonmade](https://github.com/lemonmade)! - Update testing functions from mount() => render()
## 0.5.25

@@ -4,0 +10,0 @@

{
"name": "@quilted/react-testing",
"type": "module",
"version": "0.5.25",
"version": "0.5.26",
"repository": {

@@ -6,0 +6,0 @@ "type": "git",

@@ -16,5 +16,5 @@ # `@quilted/react-testing`

- [API](#api)
- [`mount()`](#mount)
- [`createMount()`](#createMount)
- [`mount.hook()`](#mountHook)
- [`render()`](#render)
- [`createRender()`](#createRender)
- [`render.hook()`](#renderHook)
- [`destroyAll()`](#destroyAll)

@@ -44,3 +44,3 @@ - [`Root`](#root)

```tsx
import {mount} from '@quilted/react-testing';
import {render} from '@quilted/react-testing';

@@ -53,4 +53,4 @@ function PayNowButton({onPay}) {

// "Mounts" our component, running all initial lifecycle events
const payNowButton = mount(<PayNowButton onPay={pay} />);
// "Renders" our component, running all initial lifecycle events
const payNowButton = render(<PayNowButton onPay={pay} />);

@@ -65,6 +65,6 @@ // Calls our pay() function

This version of the library mounts the components into the DOM. This means that you can test components that have DOM side effects. It also means that you must ensure the DOM globals are available, typically by using a test runner’s integration with libraries like [jsdom](https://github.com/jsdom/jsdom)).
This version of the library renders the components into the DOM. This means that you can test components that have DOM side effects. It also means that you must ensure the DOM globals are available, typically by using a test runner’s integration with libraries like [jsdom](https://github.com/jsdom/jsdom)).
```tsx
import {mount} from '@quilted/react-testing/dom';
import {render} from '@quilted/react-testing/dom';

@@ -75,3 +75,3 @@ function PayNowButton({onPay}) {

const payNowButton = mount(<PayNowButton onPay={pay} />);
const payNowButton = render(<PayNowButton onPay={pay} />);
const expectedContent = payNowButton.html.includes('<button>Pay</button>');

@@ -86,3 +86,3 @@ ```

import h from 'preact';
import {mount} from '@quilted/react-testing/preact';
import {render} from '@quilted/react-testing/preact';

@@ -93,3 +93,3 @@ function PayNowButton({onPay}) {

const payNowButton = mount(<PayNowButton onPay={pay} />);
const payNowButton = render(<PayNowButton onPay={pay} />);
const expectedContent = payNowButton.html.includes('<button>Pay</button>');

@@ -117,3 +117,3 @@ ```

- [`mount`](#mount) your component with some props to get a "root" node
- [`render`](#render) your component with some props to get a "root" node
- Optionally, perform some mutation, typically by invoking a rendered component’s props with [`trigger`](#trigger)

@@ -125,3 +125,3 @@ - Make assertions based on the root, made easier with the library’s [custom matchers](#matchers)

```tsx
import {mount} from '@quilted/react-testing';
import {render} from '@quilted/react-testing';
import ClickCounter from './ClickCounter';

@@ -131,3 +131,3 @@

it('triggers handlers', () => {
const clickCounter = mount(<ClickCounter defaultCount={0} />);
const clickCounter = render(<ClickCounter defaultCount={0} />);
clickCounter.find('button').trigger('onClick');

@@ -156,3 +156,3 @@ clickCounter.find('button').trigger('onClick');

```tsx
import {mount} from '@quilted/react-testing';
import {render} from '@quilted/react-testing';
import ClickCounter from './ClickCounter';

@@ -163,3 +163,3 @@ import LinkComponent from './LinkComponent';

it('renders a link to a cool website', () => {
const clickCounter = mount(<ClickCounter defaultCount={0} />);
const clickCounter = render(<ClickCounter defaultCount={0} />);
expect(wrapper).toContainReactComponent(LinkComponent, {

@@ -171,3 +171,3 @@ to: 'https://www.cool-website.com',

it('triggers handlers', () => {
const clickCounter = mount(<ClickCounter defaultCount={0} />);
const clickCounter = render(<ClickCounter defaultCount={0} />);
clickCounter.find('button').trigger('onClick');

@@ -188,3 +188,3 @@ clickCounter.find('button').trigger('onClick');

const button = mount(<Button>Hello!</Button>);
const button = render(<Button>Hello!</Button>);
expect(button).toContainReactHtml('<button>Hello!</button>');

@@ -195,55 +195,55 @@ ```

#### <a name="mount"></a> `mount(element: ReactElement<any>)`
#### <a name="render"></a> `render(element: ReactElement<any>)`
Mounts a component to the DOM and returns a [`Root`](#root) instance. Note that for this to work, you must have a simulated browser environment, such as the `jsdom` environment that Jest uses.
Renders a component to the DOM and returns a [`Root`](#root) instance. Note that for this to work, you must have a simulated browser environment, such as the `jsdom` environment that Jest uses.
#### <a name="createMount"></a> `createMount<MountOptions, Context, Actions, Async>(options: CreateMountOptions<MountOptions, Context, Actions, Async>): MountFunction`
#### <a name="createRender"></a> `createRender<RenderOptions, Context, Actions, Async>(options: CreateRenderOptions<RenderOptions, Context, Actions, Async>): RenderFunction`
The [`mount`](#mount) function is powerful on its own, but applications will often want a more powerful version tailored to their application. A common example is app-wide context, where a set of context providers are generally assumed to be present for every component under test.
The [`render`](#render) function is powerful on its own, but applications will often want a more powerful version tailored to their application. A common example is app-wide context, where a set of context providers are generally assumed to be present for every component under test.
`createMount` enables this kind of customization by vending a custom `mount` function that will automatically wrap the component under test in an appropriate test wrapper. This custom mount function can do four things:
`createRender` enables this kind of customization by vending a custom `render` function that will automatically wrap the component under test in an appropriate test wrapper. This custom render function can do four things:
1. Allow custom options to be passed as the second argument to mount, as specified by the `MountOptions` generic
1. Allow custom options to be passed as the second argument to render, as specified by the `RenderOptions` generic
1. Map passed options to an object containing all the relevant `context` (be it objects passed through react context providers, or other useful values for controlling the test harness), and another object for helpful test `actions`
1. Use the resolved context to render react components around the element under test that use the context
1. Perform some additional resolution after the component has mounted, including asynchronous behavior like resolving initial API results
1. Perform some additional resolution after the component has rendered, including asynchronous behavior like resolving initial API results
These features are controlled by the generic type arguments to `createMount`, and the options detailed in the section below. Note that, no matter how many context providers or test wrapper you end up rendering your element within, all of the methods on the returned [`Root`](#root) instance will still be scoped to within the tree actually under test.
These features are controlled by the generic type arguments to `createRender`, and the options detailed in the section below. Note that, no matter how many context providers or test wrapper you end up rendering your element within, all of the methods on the returned [`Root`](#root) instance will still be scoped to within the tree actually under test.
##### `context(options: MountOptions): Context`
##### `context(options: RenderOptions): Context`
Takes an object of options passed by a user of your custom mount (or an empty object), and should return an object containing the context you need for the test harness. If your `Context` type has non-optional keys, you **must** provide this option.
Takes an object of options passed by a user of your custom render (or an empty object), and should return an object containing the context you need for the test harness. If your `Context` type has non-optional keys, you **must** provide this option.
##### `render(element: ReactElement, context: Context, options: MountOptions): ReactElement`
##### `render(element: ReactElement, context: Context, options: RenderOptions): ReactElement`
This function is called with the react element under test, the context created by `context()` (or an empty object), and the options passed by the user of your custom mount (or an empty object). This function must return a new react element, usually by wrapping the component in context providers.
This function is called with the react element under test, the context created by `context()` (or an empty object), and the options passed by the user of your custom render (or an empty object). This function must return a new react element, usually by wrapping the component in context providers.
> **Note:** `render` can be called multiple times for a given component. Your `render` function (and any wrapping elements you put around the element under test) should be able to re-render from calling this function, ideally without unmounting the component under test.
> **Note:** `render` can be called multiple times for a given component. Your `render` function (and any wrapping elements you put around the element under test) should be able to re-render from calling this function, ideally without unrendering the component under test.
##### `actions(root: CustomRoot, options: MountOptions): Actions`
##### `actions(root: CustomRoot, options: RenderOptions): Actions`
Takes the [root node](#root) of the tree and any mount options that were provided, and returns an object with any helpers that you need for the test harness.
Takes the [root node](#root) of the tree and any render options that were provided, and returns an object with any helpers that you need for the test harness.
If your `Action` type has non-optional keys, you **must** provide this option.
##### `afterMount(root: CustomRoot, options: MountOptions): Promise | void`
##### `afterRender(root: CustomRoot, options: RenderOptions): Promise | void`
This function allows you to perform additional logic after a component has been mounted. It gets called with a special [`Root`](#root) instance that has one additional property: `context`, the object with the context you created in `context()` (or an empty object). You can use this hook to perform some additional resolution after the component has mounted, such as resolving all GraphQL.
This function allows you to perform additional logic after a component has been rendered. It gets called with a special [`Root`](#root) instance that has one additional property: `context`, the object with the context you created in `context()` (or an empty object). You can use this hook to perform some additional resolution after the component has rendered, such as resolving all GraphQL.
If this option returns a `Promise`, the result of calling `mount()` will become a promise that resolves to the custom `Root` instance. Otherwise, it will synchronously return the `Root` instance. If you specify the `Async` generic argument as `true`, you **must** pass this option.
If this option returns a `Promise`, the result of calling `render()` will become a promise that resolves to the custom `Root` instance. Otherwise, it will synchronously return the `Root` instance. If you specify the `Async` generic argument as `true`, you **must** pass this option.
##### Extending a custom mount function
##### Extending a custom render function
It is possible to extend a custom mount function with additional logic. This can help to provide more focused testing utilities for a section of the application that provides additional context to its subtree. Every function created by `createMount` has an `extend` method. This method has the same type parameters and options as `createMount` itself. When you create an extended mount function, your additional options are merged with the original mount’s options as follows:
It is possible to extend a custom render function with additional logic. This can help to provide more focused testing utilities for a section of the application that provides additional context to its subtree. Every function created by `createRender` has an `extend` method. This method has the same type parameters and options as `createRender` itself. When you create an extended render function, your additional options are merged with the original render’s options as follows:
- The resulting `mount` function accepts the merged set of allowed options.
- The root created by the resulting `mount` function has a `context` property that is the merged result of calling the original context and the extended context.
- The `context()` and `render()` options you provide to `mount.extend()` will be called with the full, merged set of options.
- The `render()` option provided to `mount.extend()` is called **first**. The result of calling this function is then passed to the original `render()`.
- The `afterMount()` option provided to `mount.extend()` is called **first**. If it returns a promise, the resulting post-mount process will wait for it to resolve, and will then return the result of calling the original `afterMount()`. If either the original options or the extended options return a promise from `afterMount`, the resulting mount function will be asynchronous.
- The resulting `render` function accepts the merged set of allowed options.
- The root created by the resulting `render` function has a `context` property that is the merged result of calling the original context and the extended context.
- The `context()` and `render()` options you provide to `render.extend()` will be called with the full, merged set of options.
- The `render()` option provided to `render.extend()` is called **first**. The result of calling this function is then passed to the original `render()`.
- The `afterRender()` option provided to `render.extend()` is called **first**. If it returns a promise, the resulting post-render process will wait for it to resolve, and will then return the result of calling the original `afterRender()`. If either the original options or the extended options return a promise from `afterRender`, the resulting render function will be asynchronous.
Additionally, a new option is available for `extend()`: you can provide an `options` callback that receives as an argument the merged set of options, and must return a partial subset of those options to use as overrides. This can be used to extend a mount function and provide default values for some options that do not otherwise have defaults, or to customize base options on the basis of your newly-added options.
Additionally, a new option is available for `extend()`: you can provide an `options` callback that receives as an argument the merged set of options, and must return a partial subset of those options to use as overrides. This can be used to extend a render function and provide default values for some options that do not otherwise have defaults, or to customize base options on the basis of your newly-added options.
```tsx
import {createMount} from '@quilted/react-testing';
import {createRender} from '@quilted/react-testing';

@@ -258,3 +258,3 @@ interface Options {

const mount = createMount<Options, Options>({
const render = createRender<Options, Options>({
context: (options) => options,

@@ -266,3 +266,3 @@ render: (element, {pathname}) => (

const extendedMount = mount.extend<ExtendedOptions, ExtendedOptions>({
const extendedRender = render.extend<ExtendedOptions, ExtendedOptions>({
context: (options) => options,

@@ -274,3 +274,3 @@ render: (element, {graphQLResult}) => (

const mounted = extendedMount(<MyComponent />, {
const rendered = extendedRender(<MyComponent />, {
pathname: '/',

@@ -284,12 +284,12 @@ graphQLResult: {},

// It also has a context field that merged the two `context()`
// results: typeof mounted.context === {pathname: string; graphQLResult: object}
// results: typeof rendered.context === {pathname: string; graphQLResult: object}
```
#### <a name="mountHook"></a> `mount.hook<HookResult>(useHook: HookResult, options?: MountOptions): HookRunner<HookResult, Context, Actions>`
#### <a name="renderHook"></a> `render.hook<HookResult>(useHook: HookResult, options?: RenderOptions): HookRunner<HookResult, Context, Actions>`
Whenever possible, you should use test on component boundaries using [`mount()`](#mount) and the [`Root`](#root) and [`Node`](#node) objects it creates. Sometimes, you might have a particularly complex bit of logic that you encapsulate in a custom hook. Every `mount()`, including [custom mount functions](#customMount), provide a `hook()` method to run your hook in a simulated component, and to access the current return result of your hook. Below, you can see how we can use this helper to inspect our custom hook’s initial result:
Whenever possible, you should use test on component boundaries using [`render()`](#render) and the [`Root`](#root) and [`Node`](#node) objects it creates. Sometimes, you might have a particularly complex bit of logic that you encapsulate in a custom hook. Every `render()`, including [custom render functions](#customRender), provide a `hook()` method to run your hook in a simulated component, and to access the current return result of your hook. Below, you can see how we can use this helper to inspect our custom hook’s initial result:
```ts
import {useState} from 'react';
import {mount} from '@quilted/react-testing';
import {render} from '@quilted/react-testing';

@@ -302,3 +302,3 @@ function useIncrementingNumber(initial: number) {

const incrementingNumber = mount.hook(() => useIncrementingNumber(5));
const incrementingNumber = render.hook(() => useIncrementingNumber(5));
incrementingNumber.value[0]; // Our initial number, `5` in this case

@@ -310,3 +310,3 @@ ```

```ts
const incrementingNumber = mount.hook(() => useIncrementingNumber(5));
const incrementingNumber = render.hook(() => useIncrementingNumber(5));

@@ -320,11 +320,11 @@ incrementingNumber.act(([currentNumber, incrementNumber]) => {

If the “base” `mount` you used was created using `createMount()`, the second argument to its `hook()` method can be any options you could pass as the second argument to `mount()` itself. The resulting `hook` runner will also have the same [`context`](#context) and [`actions`](#actions) properties as a mounted component would have. If the base `mount` is asynchronous, `hook()` is asynchronous as well.
If the “base” `render` you used was created using `createRender()`, the second argument to its `hook()` method can be any options you could pass as the second argument to `render()` itself. The resulting `hook` runner will also have the same [`context`](#context) and [`actions`](#actions) properties as a rendered component would have. If the base `render` is asynchronous, `hook()` is asynchronous as well.
#### <a name="destroyAll"></a> `destroyAll()`
All mounted components are tracked in-memory. `destroyAll()` forcibly unmounts all mounted components and removes the DOM node used to house them. You should run this after each test that mounts a component (this is often done in a global `afterEach` hook).
All rendered components are tracked in-memory. `destroyAll()` forcibly unrenders all rendered components and removes the DOM node used to house them. You should run this after each test that renders a component (this is often done in a global `afterEach` hook).
#### <a name="root"></a> `Root<Props>`
A `Root` object represents a mounted react tree. Most of the properties and methods it exposes are simply forwarded to the [`Node`](#element) instance representing the top-level component you rendered:
A `Root` object represents a rendered react tree. Most of the properties and methods it exposes are simply forwarded to the [`Node`](#element) instance representing the top-level component you rendered:

@@ -352,13 +352,13 @@ - [#children](#children)

#### <a name="root-mount"></a> `mount()`
#### <a name="root-render"></a> `render()`
Re-mounts the component to the DOM. If the component is already mounted, this method will throw an error.
Re-renders the component to the DOM. If the component is already rendered, this method will throw an error.
##### <a name="unmount"></a> `unmount()`
##### <a name="unrender"></a> `unrender()`
Unmounts the component from the DOM. If the component is not already mounted, this method will throw an error. This method can be useful for testing side effects that occur in `componentWillUnmount` or `useEffect` hooks.
Unrenders the component from the DOM. If the component is not already rendered, this method will throw an error. This method can be useful for testing side effects that occur in `componentWillUnrender` or `useEffect` hooks.
##### <a name="setProps"></a> `setProps(props: Partial<Props>)`
Allows you to change a subset of the props specified when the component was originally mounted. This can be useful to test behaviour that is only caused by a change in props, such as `getDerivedStateFromProps` or its equivalent `useRef`/ `useState` hook version.
Allows you to change a subset of the props specified when the component was originally rendered. This can be useful to test behaviour that is only caused by a change in props, such as `getDerivedStateFromProps` or its equivalent `useRef`/ `useState` hook version.

@@ -385,3 +385,3 @@ ##### <a name="act"></a> `act<T>(action: () => T): T`

const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -397,3 +397,3 @@ // If you don’t do this, you’ll see a warning and the subsequent assertion

Unmounts the component and removes its associated DOM node. This method ensures that nothing leaks between tests. It is called on all un-destroyed `Root` objects when you call [`destroyAll()`](#destroyAll)
Unrenders the component and removes its associated DOM node. This method ensures that nothing leaks between tests. It is called on all un-destroyed `Root` objects when you call [`destroyAll()`](#destroyAll)

@@ -461,3 +461,3 @@ ##### <a name="forceUpdate"></a> `forceUpdate()`

const wrapper = mount(<Wrapper />);
const wrapper = render(<Wrapper />);
expect(wrapper.find(MyComponent).prop('name')).toBe('Michelle');

@@ -512,3 +512,3 @@

const wrapper = mount(<Wrapper />);
const wrapper = render(<Wrapper />);
expect(wrapper.find(MyComponent)).not.toBeNull();

@@ -538,3 +538,3 @@ expect(wrapper.find(YourComponent)).toBe(null);

const wrapper = mount(<Wrapper />);
const wrapper = render(<Wrapper />);
expect(wrapper.find(MyComponent, {name: 'Gord'})!.props).toMatchObject({

@@ -576,3 +576,3 @@ name: 'Gord',

const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);
myComponent.findContext(AuthContext)!.logout();

@@ -587,3 +587,3 @@

Simulates a function prop being called on your component. This is usually the key to effective tests: after you have mounted your component, you simulate a change in a subcomponent, and assert that the resulting react tree is in the expected shape. This method automatically uses [`Root#act`](#act) when calling the prop, so updates will automatically be applied to the root component.
Simulates a function prop being called on your component. This is usually the key to effective tests: after you have rendered your component, you simulate a change in a subcomponent, and assert that the resulting react tree is in the expected shape. This method automatically uses [`Root#act`](#act) when calling the prop, so updates will automatically be applied to the root component.

@@ -614,3 +614,3 @@ When you pass a key that is a prop on your component with a function type, this function will ensure that you pass arguments that are deeply partial versions of the types the prop expects. This allows you to, for example, pass an event object with only a few properties set to a `button`’s `onClick` prop. `trigger` returns whatever the result was of calling the prop.

const wrapper = mount(<Wrapper />);
const wrapper = render(<Wrapper />);
wrapper.find(MyComponent)!.trigger('onClick', 'some-id');

@@ -634,3 +634,3 @@ expect(wrapper.find('div')!.text()).toContain('some-id');

const spy = jest.fn();
const myComponent = mount(
const myComponent = render(
<MyComponent action={{label: 'Hi', onAction: spy}} />,

@@ -644,3 +644,3 @@ );

Returns a text representation of either the root node, or any element within the mounted graph. `debug()` output can be tweaked using the `options` parameter.
Returns a text representation of either the root node, or any element within the rendered graph. `debug()` output can be tweaked using the `options` parameter.

@@ -672,3 +672,3 @@ - `allProps` overrides the default props filtering behaviour and instead includes all props in the output, by default `className`, `aria-*`, and `data-*` props are omitted.

const wrapper = mount(<MyComponent />);
const wrapper = render(<MyComponent />);
// print the whole structure with one level of prop verbosity

@@ -691,3 +691,3 @@ console.log(wrapper.debug());

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -707,3 +707,3 @@ expect(myComponent.find('div')).toHaveReactProps({'aria-label': 'Hello world'});

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -720,3 +720,3 @@ expect(myComponent.find('div')).toHaveReactDataProps({

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -734,3 +734,3 @@ expect(myComponent).toContainReactComponent('div', {

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -759,3 +759,3 @@ expect(myComponent).toContainReactComponentTimes('div', 5, {

const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);

@@ -772,3 +772,3 @@ expect(myComponent).toProvideReactContext(MyContext, {

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);
expect(myComponent).toContainReactText('Hello world!');

@@ -784,3 +784,3 @@ ```

```tsx
const myComponent = mount(<MyComponent />);
const myComponent = render(<MyComponent />);
expect(myComponent).toContainHtml('<span>Hello world!</span>');

@@ -797,3 +797,3 @@ ```

- It has a very large API surface area, much of which does not conform to Shopify’s [testing conventions](https://github.com/Shopify/web-foundation/blob/master/handbook/Best%20practices/React/Testing.md). For example, Enzyme provides APIs like `setState` which encourage reaching in to implementation details of your components.
- Enzyme is unlikely to add features we use or need in a testing library, such as automatic unmounting and a built-in version `trigger()`.
- Enzyme is unlikely to add features we use or need in a testing library, such as automatic unrendering and a built-in version `trigger()`.

@@ -800,0 +800,0 @@ ### Why not use [react-testing-library](https://github.com/testing-library/react-testing-library) instead?

@@ -1,2 +0,2 @@

import {h, render, options} from 'preact';
import {h, render as renderPreact, options} from 'preact';
import type {VNode, ComponentChild, Component} from 'preact';

@@ -7,3 +7,14 @@ import {createPortal} from 'preact/compat';

import {createEnvironment, isNode} from '../environment';
import type {CustomMount, EnvironmentOptions} from '../environment';
import type {
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
} from '../environment';
import type {Node, NodeApi, Root, RootApi, HtmlNodeExtensions} from '../types';

@@ -16,5 +27,21 @@

export {isNode};
export type {Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomMount};
export type {
Node,
NodeApi,
Root,
RootApi,
HtmlNodeExtensions,
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
};
const {mount, createMount, mounted, unmountAll} = createEnvironment<
const {render, createRender, rendered, destroyAll} = createEnvironment<
Context,

@@ -28,3 +55,3 @@ HtmlNodeExtensions

render(tree, element);
renderPreact(tree, element);

@@ -34,3 +61,3 @@ return {element};

unmount({element}) {
render(null, element);
renderPreact(null, element);
element.remove();

@@ -43,3 +70,3 @@ },

export {mount, createMount, mounted, unmountAll};
export {render, createRender, rendered, destroyAll};

@@ -46,0 +73,0 @@ type Create = Parameters<

@@ -5,3 +5,14 @@ import {createRoot, type Root as ReactDomRoot} from 'react-dom/client';

import {createEnvironment, isNode} from '../environment';
import type {CustomMount, EnvironmentOptions} from '../environment';
import type {
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
} from '../environment';
import type {Node, NodeApi, Root, RootApi, HtmlNodeExtensions} from '../types';

@@ -18,5 +29,21 @@

export {isNode};
export type {Node, NodeApi, Root, RootApi, HtmlNodeExtensions, CustomMount};
export type {
Node,
NodeApi,
Root,
RootApi,
HtmlNodeExtensions,
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
};
const {mount, createMount, mounted, unmountAll} = createEnvironment<
const {render, createRender, rendered, destroyAll} = createEnvironment<
Context,

@@ -44,3 +71,3 @@ HtmlNodeExtensions

export {mount, createMount, mounted, unmountAll};
export {render, createRender, rendered, destroyAll};

@@ -47,0 +74,0 @@ type Create = Parameters<

@@ -9,4 +9,28 @@ import {

import {createEnvironment} from '../environment';
import type {EnvironmentOptions} from '../environment';
import type {
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
} from '../environment';
export type {
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,
ContextOption,
RenderOption,
ActionsOption,
};
interface Context {

@@ -16,15 +40,17 @@ renderer: ReactTestRenderer;

const {mount, createMount, mounted, unmountAll} = createEnvironment<Context>({
act,
mount(tree) {
const renderer = createTestRenderer(tree);
return {renderer};
const {render, createRender, rendered, destroyAll} = createEnvironment<Context>(
{
act,
mount(tree) {
const renderer = createTestRenderer(tree);
return {renderer};
},
unmount({renderer}) {
renderer.unmount();
},
update(_, create, {renderer}) {
return createNodeFromTestInstance(renderer.root, create) as any;
},
},
unmount({renderer}) {
renderer.unmount();
},
update(_, create, {renderer}) {
return createNodeFromTestInstance(renderer.root, create) as any;
},
});
);

@@ -55,2 +81,2 @@ type Create = Parameters<EnvironmentOptions<any>['update']>[1];

export {mount, createMount, mounted, unmountAll};
export {render, createRender, rendered, destroyAll};

@@ -8,4 +8,8 @@ // @see https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#configuring-your-testing-environment

export type {
CustomMount,
CustomRender,
CustomRenderResult,
CustomRenderOptions,
CustomRenderExtendOptions,
HookRunner,
Environment,
EnvironmentOptions,

@@ -12,0 +16,0 @@ ContextOption,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc