diff --git a/docs/angular-testing-library/api.mdx b/docs/angular-testing-library/api.mdx
index de6b61298..08daa3359 100644
--- a/docs/angular-testing-library/api.mdx
+++ b/docs/angular-testing-library/api.mdx
@@ -38,7 +38,8 @@ await render(AppComponent)
Instead of passing the component's type as first argument, you can also provide
a template. This practice is required to render directives but can also be
applied to components, it might even be more useful. The directive's (or
-component's) type must then be added to the `declarations`.
+component's) type must then be added to the `imports` (or `declarations` in case
+of non-standalone components).
**example with directive**:
@@ -76,77 +77,100 @@ export async function render(
## Component RenderOptions
-### `componentInputs`
-An object to set `@Input` properties of the component. Uses `setInput` to set
-the input property. Throws if the component property is not annotated with the
-`@Input` attribute.
+### `inputs`
-**default** : `{}`
+An object to set `@Input` or `input()` properties of the component.
-**example**:
+**default** : `{}`
```typescript
await render(AppComponent, {
- componentInputs: {
+ inputs: {
counterValue: 10,
+ // explicitly define aliases using `aliasedInput`
+ ...aliasedInput('someAlias', 'someValue'),
},
})
```
-### `componentOutputs`
+### `on`
-An object to set `@Output` properties of the component.
+An object with callbacks to subscribe to `EventEmitters` and `Observables` of
+the component.
**default** : `{}`
-**example**:
+```ts
+// using a manual callback
+const sendValue = (value) => { ... }
+await render(AppComponent, {
+ on: {
+ send: (value) => sendValue(value),
+ }
+})
+
+// using a (jest) spy
+const sendValueSpy = jest.fn()
-```typescript
await render(AppComponent, {
- componentOutputs: {
- clicked: (value) => { ... }
+ on: {
+ send: sendValueSpy
}
})
```
-### `componentProperties`
+### `declarations`
-An object to set `@Input` and `@Output` properties of the component. Doesn't
-have a fine-grained control as `componentInputs` and `componentOutputs`.
+A collection of components, directives and pipes needed to render the component.
+For example, nested components of the component.
-**default** : `{}`
+For more info see the
+[Angular docs](https://angular.dev/guide/ngmodules/overview#declarations).
+
+**default** : `[]`
**example**:
```typescript
await render(AppComponent, {
- componentProperties: {
- // an input
- counterValue: 10,
- // an output
- send: (value) => { ... }
- // a public property
- name: 'Tim'
- }
+ declarations: [CustomerDetailComponent, ButtonComponent],
})
```
-### `declarations`
+### `deferBlockBehavior`
-A collection of components, directives and pipes needed to render the component.
-For example, nested components of the component.
+Set the defer blocks behavior.
For more info see the
-[Angular docs](https://angular.io/api/core/NgModule#declarations).
+[Angular docs](https://angular.dev/api/core/testing/DeferBlockBehavior)
-**default** : `[]`
+**default** : `undefined` (uses `DeferBlockBehavior.Manual`, which is different
+from the Angular default of `DeferBlockBehavior.Playthrough`)
**example**:
```typescript
await render(AppComponent, {
- declarations: [CustomerDetailComponent, ButtonComponent],
+ deferBlockBehavior: DeferBlockBehavior.Playthrough,
+})
+```
+
+### `deferBlockStates`
+
+Set the initial state of a deferrable blocks in a component.
+
+For more info see the
+[Angular docs](https://angular.dev/api/core/testing/DeferBlockState)
+
+**default** : `undefined` (uses the Angular default, which is
+`DeferBlockState.Placeholder`)
+
+**example**:
+
+```typescript
+await render(FixtureComponent, {
+ deferBlockStates: DeferBlockState.Loading,
})
```
@@ -159,7 +183,7 @@ These will be provided at the component level. To inject dependencies at the
module level, use [`providers`](#providers).
For more info see the
-[Angular docs](https://angular.io/api/core/Directive#providers).
+[Angular docs](https://angular.dev/guide/di/hierarchical-dependency-injection#example-providing-services-in-component).
**default** : `[]`
@@ -255,7 +279,7 @@ modules. Adds `NoopAnimationsModule` by default if `BrowserAnimationsModule`
isn't added to the collection
For more info see the
-[Angular docs](https://angular.io/api/core/NgModule#imports).
+[Angular docs](https://angular.dev/guide/components#imports-in-the-component-decorator).
**default** : `[NoopAnimationsModule]`
@@ -276,7 +300,7 @@ These will be provided at the module level. To inject dependencies at the
component level, use [`componentProviders`](#componentProviders).
For more info see the
-[Angular docs](https://angular.io/api/core/NgModule#providers).
+[Angular docs](https://angular.dev/guide/di/dependency-injection-providers#).
**default** : `[]`
@@ -313,7 +337,7 @@ await render(AppComponent, {
The route configuration to set up the router service via
`RouterTestingModule.withRoutes`. For more info see the
-[Angular Routes docs](https://angular.io/api/router/Routes).
+[Angular Routes docs](https://angular.dev/api/router/Routes).
**default** : `[]`
@@ -342,7 +366,7 @@ A collection of schemas needed to render the component. Allowed values are
`NO_ERRORS_SCHEMA` and `CUSTOM_ELEMENTS_SCHEMA`.
For more info see the
-[Angular docs](https://angular.io/api/core/NgModule#schemas).
+[Angular docs](https://angular.dev/guide/components/advanced-configuration#custom-element-schemas).
**default** : `[]`
@@ -368,6 +392,64 @@ await render(AppComponent, {
})
```
+
+### ~~`componentInputs`~~ (deprecated)
+
+An object to set `@Input` properties of the component. Uses `setInput` to set
+the input property. Throws if the component property is not annotated with the
+`@Input` attribute.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentInputs: {
+ counterValue: 10,
+ },
+})
+```
+
+### ~~`componentOutputs`~~ (deprecated)
+
+An object to set `@Output` properties of the component.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentOutputs: {
+ clicked: (value) => { ... }
+ }
+})
+```
+
+### ~~`componentProperties`~~ (deprecated)
+
+An object to set `@Input` and `@Output` properties of the component. Doesn't
+have a fine-grained control as `inputs` and `on`.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentProperties: {
+ // an input
+ counterValue: 10,
+ // an output
+ send: (value) => { ... }
+ // a public property
+ name: 'Tim'
+ }
+})
+```
+
+
## `RenderResult`
### `container`
@@ -395,39 +477,60 @@ properties that are not defined are cleared.
```typescript
const {rerender} = await render(Counter, {
- componentProperties: {count: 4, name: 'Sarah'},
+ inputs: {count: 4, name: 'Sarah'},
})
expect(screen.getByTestId('count-value').textContent).toBe('4')
expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
-await rerender({componentProperties: {count: 7}})
+await rerender({
+ inputs: {count: 7}
+})
-// count updated to 7
+// count is updated to 7
expect(screen.getByTestId('count-value').textContent).toBe('7')
// name is undefined because it's not provided in rerender
expect(screen.getByTestId('name-value').textContent).toBeUndefined()
```
+Using `partialUpdate`, only the newly provided properties will be updated. Other
+input properties that aren't provided won't be cleared.
+
+```typescript
+const {rerender} = await render(Counter, {
+ inputs: {count: 4, name: 'Sarah'},
+})
+
+expect(screen.getByTestId('count-value').textContent).toBe('4')
+expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
+
+await rerender({inputs: {count: 7}, partialUpdate: true})
+
+// count is updated to 7
+expect(screen.getByTestId('count-value').textContent).toBe('7')
+// name is still rendered as "Sarah" because of the partial update
+expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
+```
+
### `detectChanges`
Trigger a change detection cycle for the component.
For more info see the
-[Angular docs](https://angular.io/api/core/testing/ComponentFixture#detectChanges).
+[Angular docs](https://angular.dev/api/core/testing/ComponentFixture#detectChanges).
### `debugElement`
The Angular `DebugElement` of the component.
-For more info see the [Angular docs](https://angular.io/api/core/DebugElement).
+For more info see the [Angular docs](https://angular.dev/api/core/DebugElement).
### `fixture`
The Angular `ComponentFixture` of the component.
For more info see the
-[Angular docs](https://angular.io/api/core/testing/ComponentFixture).
+[Angular docs](https://angular.dev/api/core/testing/ComponentFixture).
```typescript
const {fixture} = await render(AppComponent)
@@ -473,3 +576,22 @@ screen.getByRole('heading', {
})
queryByLabelText(/First name/i')
```
+
+### `renderDeferBlock`
+
+To test [Deferrable views](https://angular.dev/guide/defer#defer), you can make
+use of `renderDeferBlock`. `renderDeferBlock` will set the desired defer state
+for a specific deferrable block. The default value of a deferrable view is
+`Placeholder`, but you can also set the initial state while rendering the
+component.
+
+```typescript
+const {renderDeferBlock} = await render(FixtureComponent, {
+ deferBlockStates: DeferBlockState.Loading,
+})
+
+expect(screen.getByText(/loading/i)).toBeInTheDocument()
+
+await renderDeferBlock(DeferBlockState.Complete)
+expect(screen.getByText(/completed/i)).toBeInTheDocument()
+```
diff --git a/docs/angular-testing-library/examples.mdx b/docs/angular-testing-library/examples.mdx
index b24098ecd..2e44b893c 100644
--- a/docs/angular-testing-library/examples.mdx
+++ b/docs/angular-testing-library/examples.mdx
@@ -1,6 +1,6 @@
---
id: examples
-title: Examples
+title: Example
---
> Read about
@@ -8,124 +8,75 @@ title: Examples
> or follow the
> [guided example](https://timdeschryver.dev/blog/getting-the-most-value-out-of-your-angular-component-tests)
-```ts title="counter.component.ts"
-@Component({
- selector: 'counter',
- template: `
-
- Current Count: {{ counter }}
-
- `,
-})
-export class CounterComponent {
- @Input() counter = 0
-
- increment() {
- this.counter += 1
- }
-
- decrement() {
- this.counter -= 1
- }
-}
-```
-
-```ts title="counter.component.spec.ts"
-import {render, screen, fireEvent} from '@testing-library/angular'
-import {CounterComponent} from './counter.component.ts'
-
-describe('Counter', () => {
- test('should render counter', async () => {
- await render(CounterComponent, {
- componentProperties: {counter: 5},
- })
-
- expect(screen.getByText('Current Count: 5')).toBeInTheDocument()
- })
-
- test('should increment the counter on click', async () => {
- await render(CounterComponent, {
- componentProperties: {counter: 5},
- })
-
- fireEvent.click(screen.getByText('+'))
-
- expect(screen.getByText('Current Count: 6')).toBeInTheDocument()
- })
-})
-```
-
-## With Standalone Components
-
-Angular Testing Library can also test your standalone components.
-In fact, it even becomes easier because you don't have to set up the whole Angular TestBed.
-This was previously only [possible when you used Single Component Angular Modules (SCAMs)](https://timdeschryver.dev/blog/single-component-angular-modules-and-component-tests-go-hand-in-hand).
-
-Let's migrate the counter component to a standalone component, and let's take a look at how this affects the test.
+Angular Testing Library can be used with standalone components and also with Angular components that uses Modules.
+The example below shows how to test a standalone component, but the same principles apply to Angular components that uses Modules.
+In fact, there should be no difference in how you test both types of components, only the setup might be different.
```ts title="counter.component.ts"
@Component({
- selector: 'counter',
+ selector: 'app-counter',
template: `
+ {{ hello() }}
- Current Count: {{ counter }}
+ Current Count: {{ counter() }}
`,
- standalone: true,
})
export class CounterComponent {
- @Input() counter = 0
+ counter = model(0);
+ hello = input('Hi', { alias: 'greeting' });
increment() {
- this.counter += 1
+ this.counter.set(this.counter() + 1);
}
decrement() {
- this.counter -= 1
+ this.counter.set(this.counter() - 1);
}
}
```
-Just as you would've expected, this doesn't affect the test cases.
-Writing tests for standalone components remains the same as for "regular" components.
-
-```ts title="counter.component.spec.ts"
-import {render, screen, fireEvent} from '@testing-library/angular'
-import {CounterComponent} from './counter.component.ts'
+```typescript title="counter.component.spec.ts"
+import { render, screen, fireEvent, aliasedInput } from '@testing-library/angular';
+import { CounterComponent } from './counter.component';
describe('Counter', () => {
- test('should render counter', async () => {
- await render(CounterComponent, {
- componentProperties: {counter: 5},
- })
-
- expect(screen.getByText('Current Count: 5')).toBeInTheDocument()
- })
-
- test('should increment the counter on click', async () => {
+ it('should render counter', async () => {
await render(CounterComponent, {
- componentProperties: {counter: 5},
- })
-
- fireEvent.click(screen.getByText('+'))
-
- expect(screen.getByText('Current Count: 6')).toBeInTheDocument()
- })
-})
+ inputs: {
+ counter: 5,
+ // aliases need to be specified using aliasedInput
+ ...aliasedInput('greeting', 'Hello Alias!'),
+ },
+ });
+
+ expect(screen.getByText('Current Count: 5')).toBeVisible();
+ expect(screen.getByText('Hello Alias!')).toBeVisible();
+ });
+
+ it('should increment the counter on click', async () => {
+ await render(CounterComponent, { inputs: { counter: 5 } });
+
+ const incrementButton = screen.getByRole('button', { name: '+' });
+ fireEvent.click(incrementButton);
+
+ expect(screen.getByText('Current Count: 6')).toBeVisible();
+ });
+});
```
-To override an import of a standalone component for your component test, you can use the [`componentImports` property](api.mdx#componentImports).
-
## More examples
More examples can be found in the
[GitHub project](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/src/app/examples).
These examples include:
-- `@Input` and `@Output` properties
+- `input` and `output` properties
- Forms
-- Integration with services
-- And [more](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/src/app/examples)
+- Integration injected services
+- And
+ [more](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/src/app/examples)
If you're looking for an example that isn't on the list, please feel free to
-create a [new issue](https://github.com/testing-library/angular-testing-library/issues/new).
+create a
+[new issue](https://github.com/testing-library/angular-testing-library/issues/new).
diff --git a/docs/angular-testing-library/faq.mdx b/docs/angular-testing-library/faq.mdx
index b758c270c..e333db4b3 100644
--- a/docs/angular-testing-library/faq.mdx
+++ b/docs/angular-testing-library/faq.mdx
@@ -31,7 +31,8 @@ As you write your tests, keep in mind:
In general, you should avoid mocking out components (see
[the Guiding Principles section](guiding-principles.mdx)). However, if you need
-to, then try to use [ng-mocks](https://ng-mocks.sudo.eu/) and its [`MockBuilder`](https://ng-mocks.sudo.eu/extra/with-3rd-party#testing-libraryangular-and-mockbuilder).
+to, then try to use [ng-mocks](https://ng-mocks.sudo.eu/) and its
+[`MockBuilder`](https://ng-mocks.sudo.eu/extra/with-3rd-party#testing-libraryangular-and-mockbuilder).
```typescript
import {Component, NgModule} from '@angular/core'
@@ -58,7 +59,7 @@ export class AppModule {}
describe('ParentComponent', () => {
it('should not render ChildComponent when shallow rendering', async () => {
// all imports, declarations and exports of AppModule will be mocked.
- const dependencies = MockBuilder(ParentComponent, AppModule).build();
+ const dependencies = MockBuilder(ParentComponent, AppModule).build()
await render(ParentComponent, dependencies)
diff --git a/docs/angular-testing-library/intro.mdx b/docs/angular-testing-library/intro.mdx
index 421ebf013..a7820b7f0 100644
--- a/docs/angular-testing-library/intro.mdx
+++ b/docs/angular-testing-library/intro.mdx
@@ -8,9 +8,17 @@ sidebar_label: Introduction
builds on top of
[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library)
by adding APIs for working with Angular components.
+Starting from ATL version 17, you also need to install `@testing-library/dom`:
```bash npm2yarn
-npm install --save-dev @testing-library/angular
+npm install --save-dev @testing-library/angular @testing-library/dom
+```
+
+Or, you can use the `ng add` command.
+This sets up your project to use Angular Testing Library, which also includes the installation of `@testing-library/dom`.
+
+```bash
+ng add @testing-library/angular
```
- [`@testing-library/angular-testing-library` on GitHub](https://github.com/testing-library/angular-testing-library)
diff --git a/docs/angular-testing-library/version-compatibility.mdx b/docs/angular-testing-library/version-compatibility.mdx
index 18a72b301..3b64dd5d2 100644
--- a/docs/angular-testing-library/version-compatibility.mdx
+++ b/docs/angular-testing-library/version-compatibility.mdx
@@ -3,12 +3,16 @@ id: version-compatibility
title: Version compatibility
---
-An overview of the compatibility between different versions of Angular Testing Library and Angular.
-
-| Angular | Angular Testing Library |
-| ------- | ----------------------- |
-| 16.x | 13.x \|\| 14.x |
-| >= 15.1 | 13.x \|\| 14.x |
-| < 15.1 | 11.x \|\| 12.x |
-| 14.x | 11.x \|\| 12.x |
+An overview of the compatibility between different versions of Angular Testing
+Library and Angular.
+| Angular | Angular Testing Library |
+| ------- | ---------------------------------- |
+| 20.x | 18.x |
+| 19.x | 18.x, 17.x, 16.x, 15.x, 14.x, 13.x |
+| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 16.x | 14.x, 13.x |
+| >= 15.1 | 14.x, 13.x |
+| < 15.1 | 12.x, 11.x |
+| 14.x | 12.x, 11.x |
diff --git a/docs/bs-react-testing-library/examples.mdx b/docs/bs-react-testing-library/examples.mdx
index 0fe68d08a..08e96a7c9 100644
--- a/docs/bs-react-testing-library/examples.mdx
+++ b/docs/bs-react-testing-library/examples.mdx
@@ -1,6 +1,6 @@
---
id: examples
-title: Examples
+title: Example
---
You can find more bs-dom-testing-library examples at
diff --git a/docs/bs-react-testing-library/intro.mdx b/docs/bs-react-testing-library/intro.mdx
index fec630605..316ccc9b2 100644
--- a/docs/bs-react-testing-library/intro.mdx
+++ b/docs/bs-react-testing-library/intro.mdx
@@ -15,6 +15,9 @@ Bindings for several testing libraries have been ported to [ReasonML][re].
```bash npm2yarn
npm install --save-dev bs-dom-testing-library
+```
+
+```bash npm2yarn
npm install --save-dev bs-react-testing-library
```
diff --git a/docs/cypress-testing-library/intro.mdx b/docs/cypress-testing-library/intro.mdx
index 8121a5be4..3c88d4d2b 100644
--- a/docs/cypress-testing-library/intro.mdx
+++ b/docs/cypress-testing-library/intro.mdx
@@ -22,8 +22,7 @@ Add this line to your project's `cypress/support/commands.js`:
import '@testing-library/cypress/add-commands'
```
-You can now use all of `DOM Testing Library`'s `findBy`, `findAllBy`, `queryBy`
-and `queryAllBy` commands off the global `cy` object.
+You can now use some of `DOM Testing Library`'s `findBy`, and `findAllBy` commands off the global `cy` object.
[See the `About queries` docs for reference](/docs/queries/about).
> Note: the `get*` queries are not supported because for reasonable Cypress
diff --git a/docs/dom-testing-library/api-configuration.mdx b/docs/dom-testing-library/api-configuration.mdx
index d1dd72ca4..72dd2e196 100644
--- a/docs/dom-testing-library/api-configuration.mdx
+++ b/docs/dom-testing-library/api-configuration.mdx
@@ -90,14 +90,15 @@ second argument. If you're using testing-library in a browser you almost always
want to set this to `true`. Only very old browser don't support this property
(such as IE 8 and earlier). However, `jsdom` does not support the second
argument currently. This includes versions of `jsdom` prior to `16.4.0` and any
-version that logs a `not implemented` warning when calling `getComputedStyle`
+version that logs a `not implemented` warning when calling
+[`getComputedStyle`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle)
with a second argument e.g.
`window.getComputedStyle(document.createElement('div'), '::after')`. Defaults to
`false`
### `defaultHidden`
-The default value for the `hidden` option used by
+The default value for the [`hidden` option](queries/byrole#hidden) used by
[`getByRole`](queries/byrole.mdx). Defaults to `false`.
### `defaultIgnore`
@@ -110,12 +111,12 @@ Defaults to `script, style`.
### `showOriginalStackTrace`
-By default, `waitFor` will ensure that the stack trace for errors thrown by
-Testing Library is cleaned up and shortened so it's easier for you to identify
-the part of your code that resulted in the error (async stack traces are hard to
-debug). If you want to disable this, then set`showOriginalStackTrace` to
-`false`. You can also disable this for a specific call in the options you pass
-to `waitFor`.
+By default, [`waitFor`](api-async#waitfor) will ensure that the stack trace for
+errors thrown by Testing Library is cleaned up and shortened so it's easier for
+you to identify the part of your code that resulted in the error (async stack
+traces are hard to debug). If you want to disable this, then
+set`showOriginalStackTrace` to `false`. You can also disable this for a specific
+call in the options you pass to `waitFor`.
### `throwSuggestions` (experimental)
@@ -130,6 +131,35 @@ option.
screen.getByTestId('foo', {suggest: false}) // will not throw a suggestion
```
+:::note
+
+When this option is enabled, it may provide suggestions that lack an intuitive
+implementation. Typically this happens for
+[roles which cannot be named](https://w3c.github.io/aria/#namefromprohibited),
+most notably paragraphs. For instance, if you attempt to use
+[`getByText`](queries/bytext.mdx), you may encounter the following error:
+
+```
+TestingLibraryElementError: A better query is available, try this:
+ getByRole('paragraph')
+```
+
+However, there is no direct way to query paragraphs using the config object
+parameter, such as in `getByRole('paragraph', { name: 'Hello World' })`.
+
+To address this issue, you can leverage a custom function to validate the
+element's structure, as shown in the example below. More information can be
+found in the
+[GitHub issue](https://github.com/testing-library/dom-testing-library/issues/1306)
+
+```js
+getByRole('paragraph', {
+ name: (_, element) => element.textContent === 'Hello world',
+})
+```
+
+:::
+
### `testIdAttribute`
The attribute used by [`getByTestId`](queries/bytestid.mdx) and related queries.
@@ -143,5 +173,5 @@ message and container object as arguments.
### `asyncUtilTimeout`
-The global timeout value in milliseconds used by `waitFor` utilities. Defaults
-to 1000ms.
+The global timeout value in milliseconds used by [`waitFor`](api-async#waitfor)
+utilities. Defaults to 1000ms.
diff --git a/docs/dom-testing-library/api-custom-queries.mdx b/docs/dom-testing-library/api-custom-queries.mdx
index 452f518f7..8147ead50 100644
--- a/docs/dom-testing-library/api-custom-queries.mdx
+++ b/docs/dom-testing-library/api-custom-queries.mdx
@@ -3,9 +3,6 @@ id: api-custom-queries
title: Custom Queries
---
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
-
`DOM Testing Library` exposes many of the helper functions that are used to
implement the default queries. You can use the helpers to build custom queries.
For example, the code below shows a way to override the default `testId` queries
diff --git a/docs/dom-testing-library/api-debugging.mdx b/docs/dom-testing-library/api-debugging.mdx
index 514a930bc..492542440 100644
--- a/docs/dom-testing-library/api-debugging.mdx
+++ b/docs/dom-testing-library/api-debugging.mdx
@@ -32,7 +32,7 @@ value is `7000`. You will see `...` in the console, when the DOM content is
stripped off, because of the length you have set or due to default size limit.
Here's how you might increase this limit when running tests:
-```
+```bash npm2yarn
DEBUG_PRINT_LIMIT=10000 npm test
```
@@ -42,11 +42,11 @@ you'd like a solution that works for both, see
**Note**: The output of the DOM is colorized by default if your tests are
running in a node environment. However, you may sometimes want to turn off
-colors, such as in cases where the output is written to a log file for
-debugging purposes. You can use the environment variable `COLORS` to explicitly
-force the colorization off or on. For example:
+colors, such as in cases where the output is written to a log file for debugging
+purposes. You can use the environment variable `COLORS` to explicitly force the
+colorization off or on. For example:
-```
+```bash npm2yarn
COLORS=false npm test
```
@@ -57,7 +57,7 @@ you'd like a solution that works for both, see
## `prettyDOM`
Built on top of
-[`pretty-format`](https://github.com/facebook/jest/tree/master/packages/pretty-format),
+[`pretty-format`](https://github.com/jestjs/jest/tree/main/packages/pretty-format),
this helper function can be used to print out readable representation of the DOM
tree of a node. This can be helpful for instance when debugging tests.
@@ -79,7 +79,7 @@ It receives the root node to print out, an optional extra parameter to limit the
size of the resulting string, for cases when it becomes too large. It has a last
parameter which allows you to configure your formatting. In addition to the
options listed you can also pass the
-[options](https://github.com/facebook/jest/tree/master/packages/pretty-format#usage-with-options)
+[options](https://github.com/jestjs/jest/tree/main/packages/pretty-format#usage-with-options)
of `pretty-format`.
By default, ``, `` and comment nodes are ignored. You can
@@ -101,13 +101,13 @@ console.log(prettyDOM(div))
```
This function is what also powers
-[the automatic debugging output described above](#debugging).
+[the automatic debugging output described above](#automatic-logging).
## `screen.debug()`
-For convenience `screen` also exposes a `debug` method.
-This method is essentially a shortcut for `console.log(prettyDOM())`. It
-supports debugging the document, a single element, or an array of elements.
+For convenience `screen` also exposes a `debug` method. This method is
+essentially a shortcut for `console.log(prettyDOM())`. It supports debugging the
+document, a single element, or an array of elements.
```javascript
import {screen} from '@testing-library/dom'
@@ -129,8 +129,8 @@ screen.debug(screen.getAllByText('multi-test'))
## `screen.logTestingPlaygroundURL()`
For debugging using [testing-playground](https://testing-playground.com), screen
-exposes this convenient method which logs and returns a URL that can be opened in
-a browser.
+exposes this convenient method which logs and returns a URL that can be opened
+in a browser.
```javascript
import {screen} from '@testing-library/dom'
diff --git a/docs/dom-testing-library/install.mdx b/docs/dom-testing-library/install.mdx
index fef49e378..903a0325f 100644
--- a/docs/dom-testing-library/install.mdx
+++ b/docs/dom-testing-library/install.mdx
@@ -28,6 +28,7 @@ install the wrapper:
- [Puppeteer Testing Library](pptr-testing-library/intro.mdx)
- [Testcafe Testing Library](testcafe-testing-library/intro.mdx)
- [Nightwatch Testing Library](nightwatch-testing-library/intro.mdx)
+- [Solid Testing Library](solid-testing-library/intro.mdx)
## Ecosystem
diff --git a/docs/dom-testing-library/setup.mdx b/docs/dom-testing-library/setup.mdx
index ecac8e750..b3be51807 100644
--- a/docs/dom-testing-library/setup.mdx
+++ b/docs/dom-testing-library/setup.mdx
@@ -18,9 +18,10 @@ the DOM and browser APIs that runs in node. If you're not using Jest and you
would like to run your tests in Node, then you must install jsdom yourself.
There's also a package called
[global-jsdom](https://github.com/modosc/global-jsdom) which can be used to
-setup the global environment to simulate the browser APIs. Note that if you're using
-Vitest you only need to configure [`environment`](https://vitest.dev/config/#environment)
-to `jsdom` to achieve the same effect, you don't need global-jsdom.
+setup the global environment to simulate the browser APIs. Note that if you're
+using Vitest you only need to configure
+[`environment`](https://vitest.dev/config/#environment) to `jsdom` to achieve
+the same effect, you don't need global-jsdom.
First, install jsdom and global-jsdom.
diff --git a/docs/ecosystem-jest-native.mdx b/docs/ecosystem-jest-native.mdx
index a0974bf0e..b201e8a9f 100644
--- a/docs/ecosystem-jest-native.mdx
+++ b/docs/ecosystem-jest-native.mdx
@@ -4,6 +4,20 @@ title: jest-native
sidebar_label: jest-native
---
+:::warning
+
+**This package is deprecated and is no longer actively maintained.**
+
+We encourage you to migrate to React Native Testing Library, v12.4 or later,
+which includes modern
+[built-in Jest matchers](https://callstack.github.io/react-native-testing-library/docs/api/jest-matchers)
+based on the matchers for this repository. The migration process should be
+relatively straightforward, we have a
+[migration guide](https://callstack.github.io/react-native-testing-library/docs/migration/jest-matchers)
+available.
+
+:::
+
[`Jest Native`](https://github.com/testing-library/jest-native) is a companion
library for `React Native Testing Library` that provides custom element matchers
for Jest.
diff --git a/docs/example-external.mdx b/docs/example-external.mdx
index cf8a2cbdf..430e6b38f 100644
--- a/docs/example-external.mdx
+++ b/docs/example-external.mdx
@@ -4,10 +4,6 @@ title: External Examples
sidebar_label: External Examples
---
-## Code Samples
-
-- You can find more React Testing Library examples at [https://react-testing-examples.netlify.app/](https://react-testing-examples.netlify.app/).
-
## Playground
- An interactive sandbox with visual feedback on different queries
diff --git a/docs/example-formik.md b/docs/example-formik.md
index 95c14dc1c..4d2525b04 100644
--- a/docs/example-formik.md
+++ b/docs/example-formik.md
@@ -65,9 +65,12 @@ test('rendering and submitting a basic Formik form', async () => {
render()
const user = userEvent.setup()
- await user.type(screen.getByRole("textbox", { name: /first name/i }), 'John')
- await user.type(screen.getByRole("textbox", { name: /last name/i }), 'Dee')
- await user.type(screen.getByRole("textbox", { name: /email/i }), 'john.dee@someemail.com')
+ await user.type(screen.getByRole('textbox', {name: /first name/i}), 'John')
+ await user.type(screen.getByRole('textbox', {name: /last name/i}), 'Dee')
+ await user.type(
+ screen.getByRole('textbox', {name: /email/i}),
+ 'john.dee@someemail.com',
+ )
await user.click(screen.getByRole('button', {name: /submit/i}))
diff --git a/docs/example-react-intl.mdx b/docs/example-react-intl.mdx
index dc0bb5522..f9ea45597 100644
--- a/docs/example-react-intl.mdx
+++ b/docs/example-react-intl.mdx
@@ -141,4 +141,4 @@ the component in a way that will simulate the user behavior as much as posible.
| Use strings from the default language | Test is easy to read, and asserts expected default output. If you have variables in your strings, you can test that they work properly with correct output. | 1. Strings hardcoded into tests mean you have to update both tests and code for any copy changes. 2. If multiple elements have the same string/substring text, find-and-replace may be hard to use reliably. |
| Mock the translation library | If your library is difficult to use in the test environment, you can mock it so it is easier. For example, you can add the message ID as a data-attribute to the text so you can query by that. | Test code deviates from what runs in production. Tests may assert about message IDs but not enough about content, so errors are possible. |
| Use translation library in tests | Decouples strings from tests, so you can update the message files in one place without worrying about breaking tests. Can run tests in another language or multiple languages. `const buttonText = getNodeText();` | Overhead - it takes more lines of code to write the test, and you need to know the variables and message IDs to create the right strings. It's not obvious what the text actually is when you read the test code, making maintaining it harder. |
-| Use translation library + inline snapshots | Same as above, but by adding an inline snapshot of the string, you can read the test code and see what strings are in use, but easily update them with `jest -u` if the messages change. `expect(buttonText).toMatchInlineSnapshot("'My button text'")` | Tests are longer because of the extra lines. You can wrap up some of the translation-related code into a helper function to make it a little more inline-able and avoid repeating yourself, but you still need to know the message IDs and variables inside the test. |
+| Use translation library + inline snapshots | Same as above, but by adding an inline snapshot of the string, you can read the test code and see what strings are in use, but easily update them with `jest --updateSnapshot` if the messages change. `expect(buttonText).toMatchInlineSnapshot("'My button text'")` | Tests are longer because of the extra lines. You can wrap up some of the translation-related code into a helper function to make it a little more inline-able and avoid repeating yourself, but you still need to know the message IDs and variables inside the test. |
diff --git a/docs/example-react-router.mdx b/docs/example-react-router.mdx
index 530bb5f3b..76875f88c 100644
--- a/docs/example-react-router.mdx
+++ b/docs/example-react-router.mdx
@@ -168,7 +168,8 @@ export const App = () => (
```
In your tests, pass the history object as a whole to the Router component.
-**Note:** React Router v5 [only works with History v4](https://github.com/remix-run/history#documentation),
+**Note:** React Router v5
+[only works with History v4](https://github.com/remix-run/history#documentation),
so make sure you have the correct version of `history` installed.
```jsx
diff --git a/docs/guides-using-fake-timers.mdx b/docs/guides-using-fake-timers.mdx
index 68bee9c5d..bc7ab201e 100644
--- a/docs/guides-using-fake-timers.mdx
+++ b/docs/guides-using-fake-timers.mdx
@@ -10,11 +10,8 @@ flaky.
To solve these problems, or if you need to rely on specific timestamps in your
code, most testing frameworks offer the option to replace the real timers in
-your tests with fake ones. This should be used sporadically and not on a regular
-basis since using it contains some overhead.
-
-When using fake timers in your tests, all of the code inside your test uses fake
-timers.
+your tests with fake ones. This has a side effect - when using fake timers in
+your tests, _all_ of the code inside your test uses fake timers.
The common pattern to setup fake timers is usually within the `beforeEach`, for
example:
@@ -26,12 +23,10 @@ beforeEach(() => {
})
```
-When using fake timers, you need to remember to restore the timers after your
-test runs.
-
-The main reason to do that is to prevent 3rd party libraries running after your
-test finishes (e.g cleanup functions), from being coupled to your fake timers
-and use real timers instead.
+Since fake timers are mocking native timer functions, it is necessary to restore
+the timers after your test runs, just like regular mocks. This prevents fake
+timers leaking into other test cases and cleanup functions, where real timers
+are expected.
For that you usually call `useRealTimers` in `afterEach`.
@@ -51,3 +46,11 @@ afterEach(() => {
jest.useRealTimers()
})
```
+
+:::note
+
+Combining fake timers with `user-event` may cause test timeouts. Refer to
+[`advanceTimers`](user-event/options.mdx#advancetimers) option to prevent this
+issue.
+
+:::
diff --git a/docs/learning.mdx b/docs/learning.mdx
index f2a5b2fbd..6ddeb75b1 100644
--- a/docs/learning.mdx
+++ b/docs/learning.mdx
@@ -43,10 +43,14 @@ sidebar_label: Learning Material
[Daniel Afonso](https://twitter.com/danieljcafonso)
- [Testing Apollo Components Using react-testing-library](https://www.arahansen.com/testing-apollo-components-using-react-testing-library/)
by [Andrew Hansen](https://twitter.com/arahansen)
-- [Enzyme vs React Testing Library: A Migration Guide](https://claritydev.net/blog/enzyme-vs-react-testing-library-migration-guide) by [Alex Khomenko](https://claritydev.net)
-- [Testing React Hook Form With React Testing Library](https://claritydev.net/blog/testing-react-hook-form-with-react-testing-library) by [Alex Khomenko](https://claritydev.net)
-- [Improving React Testing Library Tests](https://claritydev.net/blog/improving-react-testing-library-tests) by [Alex Khomenko](https://claritydev.net)
-- [Testing Angular Challenges](https://angular-challenges.vercel.app/challenges/testing/) by [Thomas Laforge](https://twitter.com/laforge_toma)
+- [Enzyme vs React Testing Library: A Migration Guide](https://claritydev.net/blog/enzyme-vs-react-testing-library-migration-guide)
+ by [Alex Khomenko](https://claritydev.net)
+- [Testing React Hook Form With React Testing Library](https://claritydev.net/blog/testing-react-hook-form-with-react-testing-library)
+ by [Alex Khomenko](https://claritydev.net)
+- [Improving React Testing Library Tests](https://claritydev.net/blog/improving-react-testing-library-tests)
+ by [Alex Khomenko](https://claritydev.net)
+- [Testing Angular Challenges](https://angular-challenges.vercel.app/challenges/testing/)
+ by [Thomas Laforge](https://twitter.com/laforge_toma)
## Portuguese 🇧🇷
@@ -61,6 +65,7 @@ sidebar_label: Learning Material
- [TESTING en REACT 🧪 ¡Aprende DESDE CERO! Con react-testing-library y Jest](https://www.youtube.com/watch?v=KYjjtRgg_H0)
by [Midudev](https://twitter.com/midudev)
-- [Buenas prácticas con Angular Testing Library](https://dev.to/danyparedes/buenas-practicas-con-angular-testing-library-59lp) by [Danywalls](https://twitter.com/danywalls)
+- [Buenas prácticas con Angular Testing Library](https://dev.to/danyparedes/buenas-practicas-con-angular-testing-library-59lp)
+ by [Danywalls](https://twitter.com/danywalls)
Feel free to contribute more!
diff --git a/docs/marko-testing-library/api.mdx b/docs/marko-testing-library/api.mdx
index 87188faf2..0a3e91044 100644
--- a/docs/marko-testing-library/api.mdx
+++ b/docs/marko-testing-library/api.mdx
@@ -294,8 +294,8 @@ You can turn off the automatic test cleanup by importing the following module:
import '@marko/testing-library/dont-cleanup-after-each'
```
-With mocha you can use `mocha -r @marko/testing-library/dont-cleanup-after-each`
-as a shorthand.
+With mocha you can use
+`mocha --require @marko/testing-library/dont-cleanup-after-each` as a shorthand.
If you are using Jest, you can include
`setupFilesAfterEnv: ["@marko/testing-library/dont-cleanup-after-each"]` in your
diff --git a/docs/marko-testing-library/setup.mdx b/docs/marko-testing-library/setup.mdx
index 1b6280b56..37b74e8dc 100644
--- a/docs/marko-testing-library/setup.mdx
+++ b/docs/marko-testing-library/setup.mdx
@@ -80,7 +80,7 @@ To run server-side Marko tests with `mocha` you can simply run the following
command:
```console
-mocha -r marko/node-require
+mocha --require marko/node-require
```
This enables the
diff --git a/docs/nightwatch-testing-library/intro.mdx b/docs/nightwatch-testing-library/intro.mdx
index dd93b7282..4a9ceaf69 100644
--- a/docs/nightwatch-testing-library/intro.mdx
+++ b/docs/nightwatch-testing-library/intro.mdx
@@ -14,7 +14,7 @@ in [Nightwatch](https://nightwatchjs.org) for end-to-end web testing.
then just
```bash npm2yarn
-npm install -D @testing-library/nightwatch
+npm install --save-dev @testing-library/nightwatch
```
- [nightwatch-testing-library on GitHub][gh]
diff --git a/docs/preact-testing-library/api.mdx b/docs/preact-testing-library/api.mdx
index 96d373301..18dd43131 100644
--- a/docs/preact-testing-library/api.mdx
+++ b/docs/preact-testing-library/api.mdx
@@ -52,9 +52,12 @@ const {results} = render(, {options})
Unmounts the component from the container and destroys the container.
-📝 When you import anything from the library, this automatically runs after each
-test. If you'd like to disable this then set `process.env.PTL_SKIP_AUTO_CLEANUP`
-to true when running your tests.
+> This is called automatically if your testing framework (such as mocha, Jest or
+> Jasmine) injects a global `afterEach()` function into the testing environment.
+> If not, you will need to call `cleanup()` after each test.
+
+If you'd like to disable this then set `process.env.PTL_SKIP_AUTO_CLEANUP` to
+true when running your tests.
```jsx
import {render, cleanup} from '@testing-library/preact'
@@ -93,23 +96,25 @@ give you an idea of how to test using those functions.
### Example 1
```jsx
-const cb = jest.fn();
+const cb = jest.fn()
function Counter() {
- useEffect(cb);
+ useEffect(cb)
- const [count, setCount] = useState(0);
+ const [count, setCount] = useState(0)
- return ;
+ return
}
-const { container: { firstChild: buttonNode }, } = render();
+const {
+ container: {firstChild: buttonNode},
+} = render()
// Clear the first call to useEffect that the initial render triggers.
-cb.mockClear();
+cb.mockClear()
// Fire event Option 1.
-fireEvent.click(buttonNode);
+fireEvent.click(buttonNode)
// Fire event Option 2.
fireEvent(
@@ -118,11 +123,11 @@ fireEvent(
bubbles: true,
cancelable: true,
button: 0,
- })
-);
+ }),
+)
-expect(buttonNode).toHaveTextContent('1');
-expect(cb).toHaveBeenCalledTimes(1);
+expect(buttonNode).toHaveTextContent('1')
+expect(cb).toHaveBeenCalledTimes(1)
```
### Example 2
diff --git a/docs/queries/about.mdx b/docs/queries/about.mdx
index da377a7b0..53de0b1f0 100644
--- a/docs/queries/about.mdx
+++ b/docs/queries/about.mdx
@@ -317,12 +317,9 @@ can contain options that affect the precision of string matching:
- `exact`: Defaults to `true`; matches full strings, case-sensitive. When false,
matches substrings and is not case-sensitive.
- - `exact` has no effect on `regex` or `function` arguments.
- - `exact` has no effect on accessible names specified with the `name` option
- of `*byRole` queries. You can use a regex for fuzzy matching on accessible
- names.
- - In most cases using a regex instead of a string gives you more control over
- fuzzy matching and should be preferred over `{ exact: false }`.
+ - it has no effect when used together with `regex` or `function` arguments.
+ - in most cases, using a regex instead of a string combined with `{ exact: false }`
+ gives you more control over fuzzy matching so it should be preferred.
- `normalizer`: An optional function which overrides normalization behavior. See
[`Normalization`](#normalization).
diff --git a/docs/queries/byrole.mdx b/docs/queries/byrole.mdx
index 18b056ddd..53ecd33be 100644
--- a/docs/queries/byrole.mdx
+++ b/docs/queries/byrole.mdx
@@ -73,6 +73,14 @@ not replace the functionality of these attributes. For example
its description if `fancy.jpg` could not be loaded. Whether you want to assert
this functionality in your test or not is up to you.
+:::tip input type password
+
+Unfortunately, the spec defines that `` has no implicit
+role. This means that in order to query this type of element we must fallback to
+a less powerful query such as [`ByLabelText`](queries/bylabeltext.mdx).
+
+:::
+
## Options
### `hidden`
@@ -181,15 +189,15 @@ For example in
```html
```
-you can get the "👍" link by calling `getByRole('link', { current: true })` and
-the "👎" by calling `getByRole('link', { current: false })`. To learn more about
-the current state see
+you can get the "👍" link by calling `getByRole('link', { current: 'page' })`
+and the "👎" by calling `getByRole('link', { current: false })`. To learn more
+about the current state see
[ARIA `aria-current`](https://www.w3.org/TR/wai-aria-1.2/#aria-current).
### `pressed`
@@ -373,7 +381,8 @@ specific value using the `level` option
`getByRole('spinbutton', { value: { now: 5, min: 0, max: 10, text: 'medium' } })`.
Note that you don't have to specify all properties in `value`. A subset is
-sufficient e.g. `getByRole('spinbutton', { value: { now: 5, text: 'medium' } })`.
+sufficient e.g.
+`getByRole('spinbutton', { value: { now: 5, text: 'medium' } })`.
Given the example below,
@@ -416,7 +425,7 @@ getAllByRole('spinbutton', {value: {min: 0}})
```
> Every specified property in `value` must match. For example, if you query for
-> `{value: {min: 0, now: 3}}` `aria-valuemin` must be equal to 0 **AND**
+> `{value: {min: 0, now: 3}}` `aria-valuemin` must be equal to 0 **AND** >
> `aria-valuenow` must be equal to 3
> The `value` option is _only_ applicable to certain roles (check the linked MDN
@@ -461,3 +470,20 @@ You can query a specific element like this
```js
getByRole('alertdialog', {description: 'Your session is about to expire'})
```
+
+## Performance
+
+`getByRole` is the most preferred query to use as it most closely resembles the
+user experience, however the calculations it must perform to provide this
+confidence can be expensive (particularly with large DOM trees).
+
+Where test performance is a concern it may be desirable to trade some of this
+confidence for improved performance.
+
+`getByRole` performance can be improved by setting the option
+[`hidden`](#hidden) to `true` and thereby avoid expensive visibility checks.
+Note that in doing so inaccessible elements will now be included in the result.
+
+Another option may be to substitute `getByRole` for simpler `getByLabelText` and
+`getByText` queries which can be significantly faster though less robust
+alternatives.
diff --git a/docs/qwik-testing-library/api.mdx b/docs/qwik-testing-library/api.mdx
new file mode 100644
index 000000000..09edcce59
--- /dev/null
+++ b/docs/qwik-testing-library/api.mdx
@@ -0,0 +1,203 @@
+---
+id: api
+title: API
+sidebar_label: API
+---
+
+`@noma.to/qwik-testing-library` re-exports everything from
+[`@testing-library/dom`][@testing-library/dom], as well as:
+
+- [`render`](#render)
+- [`cleanup`](#cleanup)
+
+[@testing-library/dom]: ../dom-testing-library/api.mdx
+
+## `render`
+
+Render your component to the DOM with Qwik. By default, when no options are
+provided, the component will be rendered into a `` appended to
+`document.body`.
+
+```tsx
+import {render} from '@noma.to/qwik-testing-library'
+import {MockProvider} from './MockProvider'
+import {MyComponent} from './MyComponent'
+
+const result = await render(, {
+ baseElement: document.body,
+ container: document.createElement('host'),
+ wrapper: MockProvider,
+})
+```
+
+### Render Options
+
+You may also customize how Qwik Testing Library renders your component. Most of
+the time, you shouldn't need to modify these options.
+
+| Option | Description | Default |
+| ------------- | --------------------------------------------------- | -------------------------------- |
+| `container` | The container in which the component is rendered. | `document.createElement('host')` |
+| `baseElement` | The base element for queries and [`debug`](#debug). | `document.body` |
+| `queries` | [Custom Queries][custom-queries]. | N/A |
+| `wrapper` | The wrapper to provide a context to the component. | N/A |
+
+[custom-queries]: ../dom-testing-library/api-custom-queries.mdx
+
+#### `wrapper`
+
+You can wrap your component into a wrapper to provide a context and other
+functionalities needed by the component under test.
+
+```tsx
+import {render} from '@noma.to/qwik-testing-library'
+import {QwikCityMockProvider} from '@builder.io/qwik-city'
+
+await render(, {wrapper: QwikCityMockProvider})
+```
+
+### Render Results
+
+| Result | Description |
+| ----------------------------- | ---------------------------------------------------------- |
+| [`baseElement`](#baseelement) | The base DOM element used for queries. |
+| [`container`](#container) | The DOM element the component is mounted to. |
+| [`asFragment`](#asFragment) | Convert the DOM element to a `DocumentFragment`. |
+| [`debug`](#debug) | Log elements using [`prettyDOM`][pretty-dom]. |
+| [`unmount`](#unmount) | Unmount and destroy the component. |
+| [`...queries`](#queries) | [Query functions][query-functions] bound to `baseElement`. |
+
+[pretty-dom]: ../dom-testing-library/api-debugging.mdx#prettydom
+[query-functions]: ../queries/about.mdx
+
+#### `baseElement`
+
+The base DOM element that queries are bound to. Corresponds to
+`renderOptions.baseElement`. If you do not use `renderOptions.baseElement`, this
+will be `document.body`.
+
+#### `container`
+
+The DOM element the component is mounted in. Corresponds to
+`renderOptions.container`. If you do not use `renderOptions.container`, this
+will be `document.createElement('host')`. In general, avoid using `container`
+directly to query for elements; use [testing-library queries][query-functions]
+instead.
+
+#### `asFragment`
+
+Returns a `DocumentFragment` of your rendered component. This can be useful if
+you need to avoid live bindings and see how your component reacts to events.
+
+```tsx
+import {component$} from '@builder.io/qwik';
+import {render} from '@testing-library/react';
+import {userEvent} from "@testing-library/user-event";
+
+const TestComponent = component$(() => {
+ const count = useSignal(0);
+
+ return (
+
+ )
+});
+
+const {getByText, asFragment} = await render()
+const firstRender = asFragment()
+
+userEvent.click(getByText(/Click to increase/))
+
+// This will snapshot only the difference between the first render, and the
+// state of the DOM after the click event.
+// See https://github.com/jest-community/snapshot-diff
+expect(firstRender).toMatchDiffSnapshot(asFragment())
+```
+
+#### `debug`
+
+Log the `baseElement` or a given element using [`prettyDOM`][pretty-dom].
+
+:::tip
+
+If your `baseElement` is the default `document.body`, we recommend using
+[`screen.debug`][screen-debug].
+
+:::
+
+```tsx
+import {render, screen} from '@noma.to/qwik-testing-library'
+
+const {debug} = await render()
+
+const button = screen.getByRole('button')
+
+// log `document.body`
+screen.debug()
+
+// log your custom the `baseElement`
+debug()
+
+// log a specific element
+screen.debug(button)
+debug(button)
+```
+
+[screen-debug]: ../dom-testing-library/api-debugging.mdx#screendebug
+
+#### `unmount`
+
+Unmount and destroy the Qwik component.
+
+```tsx
+const {unmount} = await render()
+
+unmount()
+```
+
+#### Queries
+
+[Query functions][query-functions] bound to the [`baseElement`](#baseelement).
+If you passed [custom queries][custom-queries] to `render`, those will be
+available instead of the default queries.
+
+:::tip
+
+If your [`baseElement`](#baseelement) is the default `document.body`, we
+recommend using [`screen`][screen] rather than bound queries.
+
+:::
+
+```tsx
+import {render, screen} from '@noma.to/qwik-testing-library'
+
+const {getByRole} = await render()
+
+// query `document.body`
+const button = screen.getByRole('button')
+
+// query using a custom `target` or `baseElement`
+const button = getByRole('button')
+```
+
+[screen]: ../queries/about.mdx#screen
+
+## `cleanup`
+
+Destroy all components and remove any elements added to `document.body` or
+`renderOptions.baseElement`.
+
+```tsx
+import {render, cleanup} from '@noma.to/qwik-testing-library'
+
+// Default: runs after each test
+afterEach(() => {
+ cleanup()
+})
+
+await render()
+
+// Called manually for more control
+cleanup()
+```
diff --git a/docs/qwik-testing-library/example.mdx b/docs/qwik-testing-library/example.mdx
new file mode 100644
index 000000000..55e1b5f11
--- /dev/null
+++ b/docs/qwik-testing-library/example.mdx
@@ -0,0 +1,103 @@
+---
+id: example
+title: Example
+sidebar_label: Example
+---
+
+Below are some examples of how to use the Qwik Testing Library to test your
+Qwik components.
+
+You can also learn more about the [**queries**][tl-queries-docs] and [**user
+events**][tl-user-events-docs] to help you write your tests.
+
+[tl-queries-docs]: ../queries/about.mdx
+[tl-user-events-docs]: ../user-event/intro.mdx
+
+## Qwikstart
+
+This is a minimal setup to get you started, with line-by-line explanations.
+
+```tsx title="counter.spec.tsx"
+// import qwik-testing methods
+import {screen, render, waitFor} from '@noma.to/qwik-testing-library'
+// import the userEvent methods to interact with the DOM
+import {userEvent} from '@testing-library/user-event'
+// import the component to be tested
+import {Counter} from './counter'
+
+// describe the test suite
+describe('', () => {
+ // describe the test case
+ it('should increment the counter', async () => {
+ // setup user event
+ const user = userEvent.setup()
+ // render the component into the DOM
+ await render()
+
+ // retrieve the 'increment count' button
+ const incrementBtn = screen.getByRole('button', {name: /increment count/})
+ // click the button twice
+ await user.click(incrementBtn)
+ await user.click(incrementBtn)
+
+ // assert that the counter is now 2
+ expect(await screen.findByText(/count:2/)).toBeInTheDocument()
+ })
+})
+```
+
+## Qwik City - `server$` calls
+
+If one of your Qwik components uses `server$` calls, your tests might fail with
+a rather cryptic message (e.g.
+`QWIK ERROR __vite_ssr_import_0__.myServerFunctionQrl is not a function` or
+`QWIK ERROR Failed to parse URL from ?qfunc=DNpotUma33o`).
+
+We're happy to discuss it on [Discord][discord], but we consider this failure to
+be a good thing: your components should be tested in isolation, so you will be
+forced to mock your server functions.
+
+[discord]: https://qwik.dev/chat
+
+Here is an example of how to test a component that uses `server$` calls:
+
+```ts title="~/server/blog-post.ts"
+import {server$} from '@builder.io/qwik-city'
+import {BlogPost} from '~/lib/blog-post'
+
+export const getLatestPosts$ = server$(function (): Promise {
+ // get the latest posts
+ return Promise.resolve([])
+})
+```
+
+```tsx title="~/components/latest-post-list.tsx"
+import {render, screen, waitFor} from '@noma.to/qwik-testing-library'
+import {LatestPostList} from './latest-post-list'
+
+vi.mock('~/server/blog-posts', () => ({
+ // the mocked function should end with `Qrl` instead of `$`
+ getLatestPostsQrl: () => {
+ return Promise.resolve([
+ {id: 'post-1', title: 'Post 1'},
+ {id: 'post-2', title: 'Post 2'},
+ ])
+ },
+}))
+
+describe('', () => {
+ it('should render the latest posts', async () => {
+ await render()
+
+ expect(await screen.findAllByRole('listitem')).toHaveLength(2)
+ })
+})
+```
+
+Notice how the mocked function is ending with `Qrl` instead of `$`, despite
+being named as `getLatestPosts$`. This is caused by the Qwik optimizer renaming
+it to `Qrl`. So, we need to mock the `Qrl` function instead of the original `$`
+one.
+
+If your function doesn't end with `$`, the Qwik optimizer will not rename it to
+`Qrl`.
diff --git a/docs/qwik-testing-library/faq.mdx b/docs/qwik-testing-library/faq.mdx
new file mode 100644
index 000000000..5f380e025
--- /dev/null
+++ b/docs/qwik-testing-library/faq.mdx
@@ -0,0 +1,34 @@
+---
+id: faq
+title: FAQ
+sidebar_label: FAQ
+---
+
+- [How do I test file upload?](#how-do-i-test-file-upload)
+
+---
+
+## How do I test file upload?
+
+Use the [upload][] utility from `@testing-library/user-event`. It works well in
+both [jsdom][] and [happy-dom][].
+
+```tsx
+test('upload file', async () => {
+ const user = userEvent.setup()
+
+ await render()
+ const file = new File(['hello'], 'hello.png', {type: 'image/png'})
+ const input = screen.getByLabelText(/upload file/i)
+
+ await user.upload(input, file)
+
+ expect(input.files[0]).toBe(file)
+ expect(input.files.item(0)).toBe(file)
+ expect(input.files).toHaveLength(1)
+})
+```
+
+[upload]: ../user-event/api-utility.mdx#upload
+[jsdom]: https://github.com/jsdom/jsdom
+[happy-dom]: https://github.com/capricorn86/happy-dom
diff --git a/docs/qwik-testing-library/intro.mdx b/docs/qwik-testing-library/intro.mdx
new file mode 100644
index 000000000..2af36492b
--- /dev/null
+++ b/docs/qwik-testing-library/intro.mdx
@@ -0,0 +1,32 @@
+---
+id: intro
+title: Intro
+sidebar_label: Introduction
+---
+
+[Qwik Testing Library on GitHub][gh]
+
+[gh]: https://github.com/ianlet/qwik-testing-library
+
+## The Problem
+
+You want to write maintainable tests for your [Qwik][qwik] components.
+
+[qwik]: https://qwik.dev/
+
+## This Solution
+
+The Qwik Testing Library is a lightweight library for testing Qwik components.
+It provides functions on top of `qwik` and `@testing-library/dom` so you can
+mount Qwik components and query their rendered output in the DOM. Its primary
+guiding principle is:
+
+> [The more your tests resemble the way your software is used, the more
+> confidence they can give you.][guiding-principle]
+
+[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
+
+**What this library is not**:
+
+1. A test runner or framework.
+2. Specific to a testing framework.
diff --git a/docs/qwik-testing-library/setup.mdx b/docs/qwik-testing-library/setup.mdx
new file mode 100644
index 000000000..c05ff934f
--- /dev/null
+++ b/docs/qwik-testing-library/setup.mdx
@@ -0,0 +1,153 @@
+---
+id: setup
+title: Setup
+sidebar_label: Setup
+---
+
+This module is distributed via [npm][npm] which is bundled with [node][node] and
+should be installed as one of your project's `devDependencies`:
+
+```bash npm2yarn
+npm install --save-dev @noma.to/qwik-testing-library @testing-library/dom
+```
+
+This library supports `qwik` versions `1.7.2` and above and
+`@testing-library/dom` versions `10.1.0` and above.
+
+You may also be interested in installing `@testing-library/jest-dom` and
+`@testing-library/user-event` so you can use [the custom jest
+matchers][jest-dom] and [the user event library][user-event] to test
+interactions with the DOM.
+
+```bash npm2yarn
+npm install --save-dev @testing-library/jest-dom @testing-library/user-event
+```
+
+Finally, we need a DOM environment to run the tests in. This library was tested
+(for now) only with `jsdom` so we recommend using it:
+
+```bash npm2yarn
+npm install --save-dev jsdom
+```
+
+[npm]: https://www.npmjs.com/
+[node]: https://nodejs.org
+[jest-dom]: https://github.com/testing-library/jest-dom
+[user-event]: https://github.com/testing-library/user-event
+
+## Vitest Configuration
+
+We recommend using `@noma.to/qwik-testing-library` with [Vitest][vitest] as your
+test runner.
+
+If you haven't done so already, add vitest to your project using Qwik CLI:
+
+```bash npm2yarn
+npm run qwik add vitest
+```
+
+After that, we need to configure Vitest so it can run your tests. For this,
+create a _separate_ `vitest.config.ts` so you don't have to modify your
+project's `vite.config.ts`:
+
+```ts title="vitest.config.ts"
+import {defineConfig, mergeConfig} from 'vitest/config'
+import viteConfig from './vite.config'
+
+export default defineConfig(configEnv =>
+ mergeConfig(
+ viteConfig(configEnv),
+ defineConfig({
+ // qwik-testing-library needs to consider your project as a Qwik lib
+ // if it's already a Qwik lib, you can remove this section
+ build: {
+ target: 'es2020',
+ lib: {
+ entry: './src/index.ts',
+ formats: ['es', 'cjs'],
+ fileName: (format, entryName) =>
+ `${entryName}.qwik.${format === 'es' ? 'mjs' : 'cjs'}`,
+ },
+ },
+ // configure your test environment
+ test: {
+ environment: 'jsdom',
+ setupFiles: ['./vitest.setup.ts'],
+ globals: true,
+ },
+ }),
+ ),
+)
+```
+
+For now, `qwik-testing-library` needs to consider your project as a lib. Hence,
+the `build.lib` section in the config above.
+
+As the build will try to use `./src/index.ts` as the entry point, we need to
+create it:
+
+```ts title="src/index.ts"
+/**
+ * DO NOT DELETE THIS FILE
+ *
+ * This entrypoint is needed by @noma.to/qwik-testing-library to run your tests
+ */
+```
+
+Then, create the `vitest.setup.ts` file:
+
+```ts title="vitest.setup.ts"
+// Configure DOM matchers to work in Vitest
+import '@testing-library/jest-dom/vitest'
+
+// This has to run before qdev.ts loads. `beforeAll` is too late
+globalThis.qTest = false // Forces Qwik to run as if it was in a Browser
+globalThis.qRuntimeQrl = true
+globalThis.qDev = true
+globalThis.qInspector = false
+```
+
+This setup will make sure that Qwik is properly configured. It also loads
+`@testing-library/jest-dom/vitest` in your test runner so you can use matchers
+like `expect(...).toBeInTheDocument()`.
+
+By default, Qwik Testing Library cleans everything up automatically for you. You
+can opt out of this by setting the environment variable `QTL_SKIP_AUTO_CLEANUP`
+to `true`. Then in your tests, you can call the `cleanup` function when needed.
+For example:
+
+```ts
+import {cleanup} from '@noma.to/qwik-testing-library'
+import {afterEach} from 'vitest'
+
+afterEach(cleanup)
+```
+
+Now, edit your `tsconfig.json` to declare the following global types:
+
+```diff title="tsconfig.json"
+{
+ "compilerOptions": {
+ "types": [
++ "vitest/globals",
++ "@testing-library/jest-dom/vitest"
+ ]
+ },
+ "include": ["src"]
+}
+```
+
+[vitest]: https://vitest.dev/
+
+Finally, you can add test scripts to your `package.json` to run the tests with
+Vitest
+
+```json title="package.json"
+{
+ "scripts": {
+ "test": "vitest run",
+ "test:ui": "vitest --ui",
+ "test:watch": "vitest"
+ }
+}
+```
diff --git a/docs/react-native-testing-library/example-intro.mdx b/docs/react-native-testing-library/example-intro.mdx
index 1cd3b72c0..e6724ebd1 100644
--- a/docs/react-native-testing-library/example-intro.mdx
+++ b/docs/react-native-testing-library/example-intro.mdx
@@ -33,18 +33,18 @@ function Example() {
test('examples of some things', async () => {
const expectedUsername = 'Ada Lovelace'
-
+
render()
fireEvent.changeText(screen.getByTestId('input'), expectedUsername)
fireEvent.press(screen.getByText('Print Username'))
-
+
// Using `findBy` query to wait for asynchronous operation to finish
const usernameOutput = await screen.findByTestId('printed-username')
-
+
// Using `toHaveTextContent` matcher from `@testing-library/jest-native` package.
- expect(usernameOutput).toHaveTextContent(expectedUsername);
-
+ expect(usernameOutput).toHaveTextContent(expectedUsername)
+
expect(screen.toJSON()).toMatchSnapshot()
})
```
diff --git a/docs/react-testing-library/api.mdx b/docs/react-testing-library/api.mdx
index 9d95cd584..449c76f52 100644
--- a/docs/react-testing-library/api.mdx
+++ b/docs/react-testing-library/api.mdx
@@ -12,8 +12,11 @@ as these methods:
- [`baseElement`](#baseelement)
- [`hydrate`](#hydrate)
- [`legacyRoot`](#legacyroot)
+ - [`onCaughtError`](#oncaughterror)
+ - [`onRecoverableError`](#onrecoverableerror)
- [`wrapper`](#wrapper)
- [`queries`](#queries)
+ - [`reactStrictMode`](#render-options-reactstrictmode)
- [`render` Result](#render-result)
- [`...queries`](#queries-1)
- [`container`](#container-1)
@@ -27,10 +30,16 @@ as these methods:
- [`renderHook`](#renderhook)
- [`renderHook` Options](#renderhook-options)
- [`initialProps`](#initialprops)
- - [`wrapper`](#wrapper-1)
+ - [`onCaughtError`](#oncaughterror)
+ - [`onRecoverableError`](#onrecoverableerror)
+ - [`wrapper`](#renderhook-options-wrapper)
+ - [`reactStrictMode`](#renderhook-options-reactstrictmode)
- [`renderHook` Result](#renderhook-result)
- [`result`](#result)
- [`rerender`](#rerender-1)
+ - [`unmount`](#unmount-1)
+- [`configure`](#configure)
+- [`configure` Options](#configure-options)
---
@@ -98,19 +107,35 @@ well as what is printed when you use `debug()`.
### `hydrate`
If hydrate is set to true, then it will render with
-[ReactDOM.hydrate](https://reactjs.org/docs/react-dom.html#hydrate). This may be
+[ReactDOM.hydrate](https://react.dev/reference/react-dom/hydrate#hydrate). This may be
useful if you are using server-side rendering and use ReactDOM.hydrate to mount
your components.
### `legacyRoot`
+:::warning
+
+This option is only available when tests run with React 18 and earlier.
+
+:::
+
By default we'll render with support for concurrent features (i.e.
-[`ReactDOMClient.createRoot`](https://reactjs.org/docs/react-dom-client.html#createroot)).
+[`ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot)).
However, if you're dealing with a legacy app that requires rendering like in
React 17 (i.e.
-[`ReactDOM.render`](https://reactjs.org/docs/react-dom.html#render)) then you
+[`ReactDOM.render`](https://react.dev/reference/react-dom/render)) then you
should enable this option by setting `legacyRoot: true`.
+### `onCaughtError`
+
+Callback called when React catches an error in an Error Boundary.
+Behaves the same as [`onCaughtError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `onRecoverableError`
+
+Callback called when React automatically recovers from errors.
+Behaves the same as [`onRecoverableError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
### `wrapper`
Pass a React Component as the `wrapper` option to have it rendered around the
@@ -138,6 +163,11 @@ utility functions to create custom queries.
Custom queries can also be added globally by following the
[custom render guide](setup.mdx#custom-render).
+### `render` Options `reactStrictMode`
+
+When enabled, [``](https://react.dev/reference/react/StrictMode) is rendered around the inner element.
+If defined, overrides the value of `reactStrictMode` set in [`configure`](https://testing-library.com/docs/react-testing-library/api/#configure-options).
+
## `render` Result
The `render` method returns an object that has a few properties:
@@ -167,7 +197,7 @@ The containing DOM node of your rendered React Element (rendered using
> `container.firstChild`.
>
> NOTE: When that root element is a
-> [React Fragment](https://reactjs.org/docs/fragments.html),
+> [React Fragment](https://react.dev/reference/react/Fragment),
> `container.firstChild` will only get the first child of that Fragment, not the
> Fragment itself.
@@ -288,10 +318,9 @@ expect(firstRender).toMatchDiffSnapshot(asFragment())
Unmounts React trees that were mounted with [render](#render).
-> Please note that this is done automatically if the testing framework you're
-> using supports the `afterEach` global and it is injected to your testing
-> environment (like mocha, Jest, and Jasmine). If not, you will need to do
-> manual cleanups after each test.
+> This is called automatically if your testing framework (such as mocha, Jest or
+> Jasmine) injects a global `afterEach()` function into the testing environment.
+> If not, you will need to call `cleanup()` after each test.
For example, if you're using the [ava](https://github.com/avajs/ava) testing
framework, then you would need to use the `test.afterEach` hook like so:
@@ -319,10 +348,10 @@ errors in your tests).
## `act`
This is a light wrapper around the
-[`react-dom/test-utils` `act` function](https://reactjs.org/docs/test-utils.html#act).
+[`react` `act` function](https://react.dev/reference/react/act).
All it does is forward all arguments to the act function if your version of
react supports `act`. It is recommended to use the import from
-`@testing-library/react` over `react-dom/test-utils` for consistency reasons.
+`@testing-library/react` over `react` for consistency reasons.
## `renderHook`
@@ -338,7 +367,7 @@ function renderHook<
Props,
Q extends Queries = typeof queries,
Container extends Element | DocumentFragment = HTMLElement,
- BaseElement extends Element | DocumentFragment = Container,
+ BaseElement extends Element | DocumentFragment = Container
>(
render: (initialProps: Props) => Result,
options?: RenderHookOptions,
@@ -376,8 +405,10 @@ test('returns logged in user', () => {
})
```
-> NOTE: When using `renderHook` in conjunction with the `wrapper` and `initialProps` options, the `initialProps` are not passed to the `wrapper` component.
-> To provide props to the `wrapper` component, consider a solution like this:
+> NOTE: When using `renderHook` in conjunction with the `wrapper` and
+> `initialProps` options, the `initialProps` are not passed to the `wrapper`
+> component. To provide props to the `wrapper` component, consider a solution
+> like this:
>
> ```js
> const createWrapper = (Wrapper, props) => {
@@ -385,18 +416,33 @@ test('returns logged in user', () => {
> return {children};
> };
> };
->
+>
> ...
->
+>
> {
> wrapper: createWrapper(Wrapper, { value: 'foo' }),
> }
> ```
+### `onCaughtError`
+
+Callback called when React catches an error in an Error Boundary.
+Behaves the same as [`onCaughtError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `onRecoverableError`
+
+Callback called when React automatically recovers from errors.
+Behaves the same as [`onRecoverableError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
### `renderHook` Options `wrapper`
See [`wrapper` option for `render`](#wrapper)
+
+### `renderHook` Options `reactStrictMode`
+
+See [`reactStrictMode` option for `render`](#render-options-reactstrictmode)
+
## `renderHook` Result
The `renderHook` method returns an object that has a few properties:
@@ -422,7 +468,7 @@ expect(result.current).toBe('Alice')
```
Note that the value is held in `result.current`. Think of `result` as a
-[ref](https://reactjs.org/docs/glossary.html#refs) for the most recently
+[ref](https://react.dev/learn/referencing-values-with-refs) for the most recently
**committed** value.
### `rerender`
@@ -448,4 +494,26 @@ import {renderHook} from '@testing-library/react'
const {unmount} = renderHook(({name = 'Alice'} = {}) => name)
unmount()
-```
\ No newline at end of file
+```
+
+## `configure`
+
+Changes global options. Basic usage can be seen at
+[Configuration Options](dom-testing-library/api-configuration.mdx).
+
+React Testing Library also has dedicated options.
+
+```typescript
+import {configure} from '@testing-library/react'
+
+configure({reactStrictMode: true})
+```
+
+## `configure` Options
+
+### `reactStrictMode`
+
+When enabled, [``](https://react.dev/reference/react/StrictMode) is
+rendered around the inner element. Defaults to `false`.
+
+This setting can be changed for a single test by providing `reactStrictMode` in the options argument of the [`render`](#render-options-reactstrictmode) function.
\ No newline at end of file
diff --git a/docs/react-testing-library/cheatsheet.mdx b/docs/react-testing-library/cheatsheet.mdx
index d0e3341be..a50d82543 100644
--- a/docs/react-testing-library/cheatsheet.mdx
+++ b/docs/react-testing-library/cheatsheet.mdx
@@ -124,14 +124,14 @@ See [Events API](dom-testing-library/api-events.mdx)
- **click** `fireEvent.click(node)`
- [See all supported events](https://github.com/testing-library/dom-testing-library/blob/master/src/event-map.js)
- **act** wrapper around
- [react-dom/test-utils act](https://reactjs.org/docs/test-utils.html#act);
+ [react act](https://react.dev/reference/react/act);
React Testing Library wraps render and fireEvent in a call to `act` already so
most cases should not require using it manually
## Other
See [Querying Within Elements](dom-testing-library/api-within.mdx),
-[Config API](dom-testing-library/api-configuration.mdx),
+[Config API](react-testing-library/api.mdx#configure),
[Cleanup](react-testing-library/api.mdx#cleanup),
- **within** take a node and return an object with all the queries bound to the
diff --git a/docs/react-testing-library/example-intro.mdx b/docs/react-testing-library/example-intro.mdx
index 035bfe252..66722d223 100644
--- a/docs/react-testing-library/example-intro.mdx
+++ b/docs/react-testing-library/example-intro.mdx
@@ -6,81 +6,91 @@ sidebar_label: Example
## Quickstart
-This is a minimal setup to get you started.
-If you want to see a description of what each line does,
-scroll down to the [annotated version](#quickstart-annotated).
-Scroll down to [Full Example](#full-example) to see a more advanced test setup.
+This is a minimal setup to get you started. If you want to see a description of
+what each line does, scroll down to the
+[annotated version](#quickstart-annotated). Scroll down to
+[Full Example](#full-example) to see a more advanced test setup.
```jsx
-import { render, screen } from '@testing-library/react'
+import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import '@testing-library/jest-dom'
import Fetch from './fetch'
test('loads and displays greeting', async () => {
-
// ARRANGE
render()
-
+
// ACT
await userEvent.click(screen.getByText('Load Greeting'))
await screen.findByRole('heading')
-
+
// ASSERT
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toBeDisabled()
-
})
```
Quickstart (Annotated Example)
- ```jsx
- // import react-testing methods
- import { render, screen } from '@testing-library/react'
- // userEvent library simulates user interactions by dispatching the events that would happen if the interaction took place in a browser.
- import userEvent from '@testing-library/user-event'
- // add custom jest matchers from jest-dom
- import '@testing-library/jest-dom'
- // the component to test
- import Fetch from './fetch'
-
- test('loads and displays greeting', async () => {
-
- // Render a React element into the DOM
- render()
-
- await userEvent.click(screen.getByText('Load Greeting'))
- // wait before throwing an error if it cannot find an element
- await screen.findByRole('heading')
-
- // assert that the alert message is correct using
- // toHaveTextContent, a custom matcher from jest-dom.
- expect(screen.getByRole('heading')).toHaveTextContent('hello there')
- expect(screen.getByRole('button')).toBeDisabled()
-
- })
- ```
+```jsx
+// import react-testing methods
+import {render, screen} from '@testing-library/react'
+// userEvent library simulates user interactions by dispatching the events that would happen if the interaction took place in a browser.
+import userEvent from '@testing-library/user-event'
+// add custom jest matchers from jest-dom
+import '@testing-library/jest-dom'
+// the component to test
+import Fetch from './fetch'
-
+test('loads and displays greeting', async () => {
+ // Render a React element into the DOM
+ render()
+
+ await userEvent.click(screen.getByText('Load Greeting'))
+ // wait before throwing an error if it cannot find an element
+ await screen.findByRole('heading')
+
+ // assert that the alert message is correct using
+ // toHaveTextContent, a custom matcher from jest-dom.
+ expect(screen.getByRole('heading')).toHaveTextContent('hello there')
+ expect(screen.getByRole('button')).toBeDisabled()
+})
+```
+
## Full Example
See the following sections for a detailed breakdown of the test
+:::note
+
+We recommend using the
+[Mock Service Worker (MSW)](https://github.com/mswjs/msw) library to
+declaratively mock API communication in your tests instead of stubbing
+`window.fetch`, or relying on third-party adapters.
+
+:::
+
+:::note
+
+Our example here uses axios to make its API calls. If your application uses [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) to make its API calls, then be aware that by default JSDOM does not include fetch. If you are using vitest as your test runner, it will be included for you. For jest you may wish to manually polyfill `fetch()` or use the [jest-fixed-jsdom](https://github.com/mswjs/jest-fixed-jsdom) environment which includes fetch.
+
+:::
+
```jsx title="__tests__/fetch.test.jsx"
import React from 'react'
-import {rest} from 'msw'
+import {http, HttpResponse} from 'msw'
import {setupServer} from 'msw/node'
import {render, fireEvent, screen} from '@testing-library/react'
import '@testing-library/jest-dom'
import Fetch from '../fetch'
const server = setupServer(
- rest.get('/greeting', (req, res, ctx) => {
- return res(ctx.json({greeting: 'hello there'}))
+ http.get('/greeting', () => {
+ return HttpResponse.json({greeting: 'hello there'})
}),
)
@@ -101,8 +111,8 @@ test('loads and displays greeting', async () => {
test('handles server error', async () => {
server.use(
- rest.get('/greeting', (req, res, ctx) => {
- return res(ctx.status(500))
+ http.get('/greeting', () => {
+ return new HttpResponse(null, {status: 500})
}),
)
@@ -117,10 +127,6 @@ test('handles server error', async () => {
})
```
-> We recommend using [Mock Service Worker](https://github.com/mswjs/msw) library
-> to declaratively mock API communication in your tests instead of stubbing
-> `window.fetch`, or relying on third-party adapters.
-
---
## Step-By-Step
@@ -132,7 +138,7 @@ test('handles server error', async () => {
import React from 'react'
// import API mocking utilities from Mock Service Worker
-import {rest} from 'msw'
+import {http, HttpResponse} from 'msw'
import {setupServer} from 'msw/node'
// import react-testing methods
@@ -161,9 +167,9 @@ component makes.
// declare which API requests to mock
const server = setupServer(
// capture "GET /greeting" requests
- rest.get('/greeting', (req, res, ctx) => {
+ http.get('/greeting', (req, res, ctx) => {
// respond using a mocked JSON body
- return res(ctx.json({greeting: 'hello there'}))
+ return HttpResponse.json({greeting: 'hello there'})
}),
)
@@ -181,8 +187,8 @@ test('handles server error', async () => {
server.use(
// override the initial "GET /greeting" request handler
// to return a 500 Server Error
- rest.get('/greeting', (req, res, ctx) => {
- return res(ctx.status(500))
+ http.get('/greeting', (req, res, ctx) => {
+ return new HttpResponse(null, {status: 500})
}),
)
diff --git a/docs/react-testing-library/faq.mdx b/docs/react-testing-library/faq.mdx
index 1d93c926d..b939e8e61 100644
--- a/docs/react-testing-library/faq.mdx
+++ b/docs/react-testing-library/faq.mdx
@@ -81,6 +81,57 @@ as part of the `change` method call.
+How do I test error boundaries
+
+To test if an error boundary successfully catches an error, you should make sure that if the fallback of the boundary is displayed when a child threw.
+
+Here's an example of how you can test an error boundary:
+
+```jsx
+import React from 'react'
+import {render, screen} from '@testing-library/react'
+
+class ErrorBoundary extends React.Component {
+ state = {error: null}
+ static getDerivedStateFromError(error) {
+ return {error}
+ }
+ render() {
+ const {error} = this.state
+ if (error) {
+ return
Something went wrong
+ }
+ return this.props.children
+ }
+}
+
+test('error boundary catches error', () => {
+ const {container} = render(
+
+
+ ,
+ )
+ expect(container.textContent).toEqual('Something went wrong.')
+})
+```
+
+If the error boundary did not catch the error, the test would fail since the `render` call would throw the error the Component produced.
+
+
+:::info
+
+React 18 will call `console.error` with an extended error message.
+React 19 will call `console.warn` with an extended error message.
+
+To disable the additional `console.warn` call in React 19, you can provide a custom `onCaughtError` callback e.g. `render(, {onCaughtError: () => {}})`
+`onCaughtError` is not supported in React 18.
+
+:::
+
+
+
+
+
Can I write unit tests with this library?
Definitely yes! You can write unit and integration tests with this library. See
@@ -98,6 +149,47 @@ As you write your tests, keep in mind:
+How do I test thrown errors in a Component or Hook?
+
+If a component throws during render, the origin of the state update will throw if wrapped in `act`.
+By default, `render` and `fireEvent` are wrapped in `act`.
+You can just wrap it in a try-catch or use dedicated matchers if your test runner supports these.
+For example, in Jest you can use `toThrow`:
+
+```jsx
+function Thrower() {
+ throw new Error('I throw')
+}
+
+test('it throws', () => {
+ expect(() => render()).toThrow('I throw')
+})
+```
+
+The same applies to Hooks and `renderHook`:
+
+```jsx
+function useThrower() {
+ throw new Error('I throw')
+}
+
+test('it throws', () => {
+ expect(() => renderHook(useThrower)).toThrow('I throw')
+})
+```
+
+:::info
+
+React 18 will call `console.error` with an extended error message.
+React 19 will call `console.warn` with an extended error message unless the state update is wrapped in `act`.
+`render`, `renderHook` and `fireEvent` are wrapped in `act` by default.
+
+:::
+
+
+
+
+
If I can't use shallow rendering, how do I mock out components in tests?
diff --git a/docs/react-testing-library/intro.mdx b/docs/react-testing-library/intro.mdx
index bf5c4ffda..7d8d026c4 100644
--- a/docs/react-testing-library/intro.mdx
+++ b/docs/react-testing-library/intro.mdx
@@ -7,15 +7,23 @@ sidebar_label: Introduction
[`React Testing Library`][gh] builds on top of `DOM Testing Library` by adding
APIs for working with React components.
-> Projects created with [`Create React App`](https://create-react-app.dev/) have
-> out of the box support for **React Testing Library**. If that is not the case,
-> you can add it via `npm` like so:
+## Installation
+
+To get started with `React Testing Library`, you'll need to install it together
+with its peerDependency `@testing-library/dom`:
```bash npm2yarn
-npm install --save-dev @testing-library/react
+npm install --save-dev @testing-library/react @testing-library/dom
```
-- [React Testing Library on GitHub][gh]
+### With TypeScript
+
+To get full type coverage, you need to install the types for `react` and `react-dom` as
+well:
+
+```bash npm2yarn
+npm install --save-dev @testing-library/react @testing-library/dom @types/react @types/react-dom
+```
[gh]: https://github.com/testing-library/react-testing-library
diff --git a/docs/react-testing-library/setup.mdx b/docs/react-testing-library/setup.mdx
index 552037088..7fbfa1555 100644
--- a/docs/react-testing-library/setup.mdx
+++ b/docs/react-testing-library/setup.mdx
@@ -31,10 +31,10 @@ make your test util file accessible without using relative paths.
The example below sets up data providers using the [`wrapper`](api.mdx#wrapper)
option to `render`.
-
+
-
+
```diff title="my-component.test.jsx"
- import { render, fireEvent } from '@testing-library/react';
@@ -70,7 +70,7 @@ export {customRender as render}
-
+
```diff title="my-component.test.tsx"
- import { render, fireEvent } from '@testing-library/react';
@@ -84,7 +84,7 @@ import {ThemeProvider} from 'my-ui-lib'
import {TranslationProvider} from 'my-i18n-lib'
import defaultStrings from 'i18n/en-x-default'
-const AllTheProviders = ({children}:{children: React.ReactNode}) => {
+const AllTheProviders = ({children}: {children: React.ReactNode}) => {
return (
@@ -214,7 +214,7 @@ import {
const queryAllByDataCy = (
container: HTMLElement,
id: Matcher,
- options?: MatcherOptions | undefined
+ options?: MatcherOptions | undefined,
) => queryHelpers.queryAllByAttribute('data-cy', container, id, options)
const getMultipleError = (c, dataCyValue) =>
@@ -247,13 +247,13 @@ export {
You can then override and append the new queries via the render function by
passing a [`queries`](api.mdx#render-options) option.
-If you want to add custom queries globally, you can do this by defining your customized
-`render`, `screen` and `within` methods:
+If you want to add custom queries globally, you can do this by defining your
+customized `render`, `screen` and `within` methods:
-
+
-
+
```js title="test-utils.js"
import {render, queries, within} from '@testing-library/react'
@@ -273,16 +273,12 @@ const customRender = (ui, options) =>
export * from '@testing-library/react'
// override render method
-export {
- customScreen as screen,
- customWithin as within,
- customRender as render,
-}
+export {customScreen as screen, customWithin as within, customRender as render}
```
-
+
```ts title="test-utils.ts"
import {render, queries, within, RenderOptions} from '@testing-library/react'
@@ -302,11 +298,7 @@ const customRender = (
) => render(ui, {queries: allQueries, ...options})
export * from '@testing-library/react'
-export {
- customScreen as screen,
- customWithin as within,
- customRender as render,
-}
+export {customScreen as screen, customWithin as within, customRender as render}
```
@@ -362,12 +354,15 @@ using Create React App without TypeScript, save this to `jsconfig.json` instead.
### Jest 28
-If you're using Jest 28 or later, jest-environment-jsdom package now must be installed separately.
+If you're using Jest 28 or later, jest-environment-jsdom package now must be
+installed separately.
+
```bash npm2yarn
npm install --save-dev jest-environment-jsdom
```
-`jsdom` is also no longer the default environment. You can enable `jsdom` globally by editing `jest.config.js`:
+`jsdom` is also no longer the default environment. You can enable `jsdom`
+globally by editing `jest.config.js`:
```diff title="jest.config.js"
module.exports = {
@@ -376,7 +371,8 @@ npm install --save-dev jest-environment-jsdom
}
```
-Or if you only need `jsdom` in some of your tests, you can enable it as and when needed using
+Or if you only need `jsdom` in some of your tests, you can enable it as and when
+needed using
[docblocks](https://jestjs.io/docs/configuration#testenvironment-string):
```js
@@ -387,7 +383,8 @@ Or if you only need `jsdom` in some of your tests, you can enable it as and when
### Jest 27
-If you're using a recent version of Jest (27), `jsdom` is no longer the default environment. You can enable `jsdom` globally by editing `jest.config.js`:
+If you're using a recent version of Jest (27), `jsdom` is no longer the default
+environment. You can enable `jsdom` globally by editing `jest.config.js`:
```diff title="jest.config.js"
module.exports = {
@@ -481,7 +478,7 @@ do this with Jest's `setupFiles` configuration:
Or with mocha's `-r` flag:
```
-mocha -r @testing-library/react/dont-cleanup-after-each
+mocha --require @testing-library/react/dont-cleanup-after-each
```
Alternatively, you could import `@testing-library/react/pure` in all your tests
@@ -511,5 +508,43 @@ exports.mochaHooks = {
And register it using mocha's `-r` flag:
```
-mocha -r ./mocha-watch-cleanup-after-each.js
+mocha --require ./mocha-watch-cleanup-after-each.js
+```
+
+### Auto Cleanup in Vitest
+
+If you're using Vitest and want automatic cleanup to work, you can
+[enable globals](https://vitest.dev/config/#globals) through its configuration
+file:
+
+```ts title="vitest.config.ts"
+import {defineConfig} from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ globals: true,
+ },
+})
+```
+
+If you don't want to enable globals, you can import `cleanup` and call it
+manually in a top-level `afterEach` hook:
+
+```ts title="vitest.config.ts"
+import {defineConfig} from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ setupFiles: ['vitest-cleanup-after-each.ts'],
+ },
+})
+```
+
+```ts title="vitest-cleanup-after-each.ts"
+import {cleanup} from '@testing-library/react'
+import {afterEach} from 'vitest'
+
+afterEach(() => {
+ cleanup()
+})
```
diff --git a/docs/solid-testing-library/api.mdx b/docs/solid-testing-library/api.mdx
new file mode 100644
index 000000000..659dad040
--- /dev/null
+++ b/docs/solid-testing-library/api.mdx
@@ -0,0 +1,181 @@
+---
+id: api
+title: API
+sidebar_label: API
+---
+
+Due to being inspired by the [preact-testing-library][preact-docs] you can check
+its page for more information.
+
+[preact-docs]: ../preact-testing-library/intro.mdx
+
+There are several key differences, to be aware of.
+
+- [`render`](#render)
+- [`renderHook`](#renderHook)
+- [`renderDirective`](#renderDirective)
+- [`Async methods`](#async-methods)
+- [`Known issues`](#known-issues)
+
+## `render`
+
+The `render` function takes in a function that returns a Solid Component, rather
+than simply the component itself.
+
+```tsx
+const results = render(() => , options)
+```
+
+Solid.js does _not_ re-render, it merely executes side effects triggered by
+reactive state that change the DOM, therefore there is no `rerender` method. You
+can use global signals to manipulate your test component in a way that causes it
+to update.
+
+In addition to the original API, the render function of this testing library
+supports a convenient `location` option that will set up an in-memory router
+pointing at the specified location. Since this setup is not synchronous, you
+need to first use asynchronous queries (`findBy`) after employing it:
+
+```tsx
+it('uses params', async () => {
+ const App = () => (
+ <>
+
Id: {useParams()?.id}
} />
+
Start
} />
+ >
+ )
+ const {findByText} = render(() => , {location: 'ids/1234'})
+ expect(await findByText('Id: 1234')).not.toBeFalsy()
+})
+```
+
+It uses `@solidjs/router`, so if you want to use a different router, you should
+consider the `wrapper` option instead. If you attempt to use this without having
+the package installed, you will receive an error message.
+
+## `renderHook`
+
+Solid.js external reactive state does not require any DOM elements to run in, so
+our `renderHook` call to test hooks in the context of a component (if your hook
+does not require the context of a component, `createRoot` should suffice to test
+the reactive behavior; for convenience, we also have `createEffect`, which is
+described in the [`Async methods`](#async-methods) section) has no `container`,
+`baseElement` or queries in its options or return value. Instead, it has an
+`owner` to be used with
+[`runWithOwner`](https://www.solidjs.com/docs/latest/api#runwithowner) if
+required. It also exposes a `cleanup` function, though this is already
+automatically called after the test is finished.
+
+```ts
+function renderHook(
+ hook: (...args: Args) => Result,
+ options: {
+ initialProps?: Args,
+ wrapper?: Component<{ children: JSX.Element }>
+ }
+) => {
+ result: Result;
+ owner: Owner | null;
+ cleanup: () => void;
+}
+```
+
+This can be used to easily test a hook / primitive:
+
+```ts
+const {result} = renderHook(createResult)
+expect(result).toBe(true)
+```
+
+If you are using a `wrapper` with `renderHook`, make sure it will **always**
+return `props.children` - especially if you are using a context with
+asynchronous code together with ``, because this is required to get the
+value from the hook and it is only obtained synchronously once and you will
+otherwise only get `undefined` and wonder why this is the case.
+
+## `renderDirective`
+
+Solid.js supports
+[custom directives](https://www.solidjs.com/docs/latest/api#use___), which is a
+convenient pattern to tie custom behavior to elements, so we also have a
+`renderDirective` call, which augments `renderHook` to take a directive as first
+argument, accept an `initialValue` for the argument and a `targetElement`
+(string, HTMLElement or function returning an HTMLElement) in the `options` and
+also returns `arg` and `setArg` to read and manipulate the argument of the
+directive.
+
+```ts
+function renderDirective<
+ Arg extends any,
+ Elem extends HTMLElement
+>(
+ directive: (ref: Elem, arg: Accessor) => void,
+ options?: {
+ ...renderOptions,
+ initialValue: Arg,
+ targetElement:
+ | Lowercase
+ | Elem
+ | (() => Elem)
+ }
+): Result & { arg: Accessor, setArg: Setter };
+```
+
+This allows for very effective and concise testing of directives:
+
+```ts
+const {asFragment, setArg} = renderDirective(myDirective)
+expect(asFragment()).toBe('')
+setArg('perfect')
+expect(asFragment()).toBe('')
+```
+
+## Async methods
+
+Solid.js reactive changes are pretty instantaneous, so there is rarely need to
+use `waitFor(…)`, `await findByRole(…)` and other asynchronous queries to test
+the rendered result, except for transitions, suspense, resources and router
+navigation.
+
+Solid.js manages side effects with different variants of `createEffect`. While
+you can use `waitFor` to test asynchronous effects, it uses polling instead of
+allowing Solid's reactivity to trigger the next step. In order to simplify
+testing those asynchronous effects, we have a `testEffect` helper that
+complements the hooks for directives and hooks:
+
+```ts
+testEffect(fn: (done: (result: T) => void) => void, owner?: Owner): Promise
+
+// use it like this:
+test("testEffect allows testing an effect asynchronously", () => {
+ const [value, setValue] = createSignal(0);
+ return testEffect(done => createEffect((run: number = 0) => {
+ if (run === 0) {
+ expect(value()).toBe(0);
+ setValue(1);
+ } else if (run === 1) {
+ expect(value()).toBe(1);
+ done();
+ }
+ return run + 1;
+ }));
+});
+```
+
+It allows running the effect inside a defined owner that is received as an
+optional second argument. This can be useful in combination with `renderHook`,
+which gives you an owner field in its result. The return value is a Promise with
+the value given to the `done()` callback. You can either await the result for
+further assertions or return it to your test runner.
+
+## Known issues
+
+If you are using [`vitest`](https://vitest.dev/), then tests might fail, because
+the packages `solid-js`, and `@solidjs/router` (if used) need to be loaded only
+once, and they could be loaded both through the internal `vite` server and
+through node. Typical bugs that happen because of this is that dispose is
+supposedly undefined, or the router could not be loaded.
+
+Since version 2.8.2, our vite plugin has gained the capability to configure
+everything for testing, so you should only need extra configuration for globals,
+coverage, etc.
diff --git a/docs/solid-testing-library/intro.mdx b/docs/solid-testing-library/intro.mdx
new file mode 100644
index 000000000..b1f0dafe6
--- /dev/null
+++ b/docs/solid-testing-library/intro.mdx
@@ -0,0 +1,53 @@
+---
+id: intro
+title: Intro
+sidebar_label: Introduction
+---
+
+[Solid Testing Library on GitHub][gh]
+
+> Inspired completely by [preact-testing-library][preact-docs]
+
+[preact-docs]: ../preact-testing-library/intro.mdx
+[gh]: https://github.com/solidjs/solid-testing-library
+
+```bash npm2yarn
+npm install --save-dev @solidjs/testing-library
+```
+
+> This library is built on top of
+> [`DOM Testing Library`](dom-testing-library/intro.mdx) which is where most of
+> the logic behind the queries is.
+
+## The Problem
+
+You want to write tests for your Solid components so that they avoid including
+implementation details, and are maintainable in the long run.
+
+## This Solution
+
+The Solid Testing Library is a very lightweight solution for testing Solid
+components. Its primary guiding principle is:
+
+> [The more your tests resemble the way your software is used, the more confidence they can give you.](https://twitter.com/kentcdodds/status/977018512689455106)
+
+See the [Dom introduction][dom-solution-explainer] and [React
+introduction][react-solution-explainer] for a more in-depth explanation.
+
+[dom-solution-explainer]: ../dom-testing-library/intro.mdx#this-solution
+[react-solution-explainer]: ../react-testing-library/intro.mdx#this-solution
+
+**What this library is not**:
+
+1. A test runner or framework.
+2. Specific to a testing framework.
+
+If you using Jest we recommend using
+[solid-jest](https://github.com/solidjs/solid-jest) to properly resolve the
+browser version of Solid as Jest will default to the server version when run in
+Node.
+
+💡 If you are using Jest or vitest, you may also be interested in installing
+`@testing-library/jest-dom` so you can use [the custom jest matchers][jest-dom].
+
+[jest-dom]: ../ecosystem-jest-dom
diff --git a/docs/svelte-testing-library/api.mdx b/docs/svelte-testing-library/api.mdx
index 7af1ac643..4355dc682 100644
--- a/docs/svelte-testing-library/api.mdx
+++ b/docs/svelte-testing-library/api.mdx
@@ -4,147 +4,306 @@ title: API
sidebar_label: API
---
-- [`@testing-library/dom`](#testing-library-dom)
+`@testing-library/svelte` re-exports everything from
+[`@testing-library/dom`][@testing-library/dom], as well as:
+
- [`render`](#render)
+- [`act`](#act)
- [`cleanup`](#cleanup)
-- [`act`](#act-async)
-- [`fireEvent`](#fireevent-async)
-
----
+- [`fireEvent` (async)](#fireevent-async)
-## `@testing-library/dom`
-
-This library re-exports everything from the DOM Testing Library
-(`@testing-library/dom`). See the [documentation](queries/about.mdx) to see what
-goodies you can use.
-
-📝 `fireEvent` is an `async` method when imported from
-`@testing-library/svelte`. This is because it calls [`tick`][svelte-tick] which
-tells Svelte to apply any new changes to the DOM.
+[@testing-library/dom]: ../dom-testing-library/api.mdx
## `render`
+Render your component to the DOM with Svelte. By default, the component will be
+rendered into a `
` appended to `document.body`.
+
```js
import {render} from '@testing-library/svelte'
+import MyComponent from './MyComponent.svelte'
-const {results} = render(YourComponent, {ComponentOptions}, {RenderOptions})
+const result = render(MyComponent, componentOptions, renderOptions)
```
### Component Options
-These are the options you pass when instantiating your Svelte `Component`.
-Please refer to the
-[Client-side component API](https://svelte.dev/docs#run-time-client-side-component-api-creating-a-component).
+You may customize how the component is created and mounted. These options are
+passed directly to Svelte.
-📝 If the only option you're passing in is `props`, then you can just pass them
-in directly.
+If you only need to send props to your component, you may pass props directly,
+as long as those props don't share a name with a component option.
```js
-// With options.
-const {results} = render(YourComponent, {
- target: MyTarget,
+// pass props to the component
+render(YourComponent, {myProp: 'value'})
+
+// pass props and other options to the component
+render(YourComponent, {
props: {myProp: 'value'},
+ context: new Map([[('theme': 'dark')]]),
})
-
-// Props only.
-const {results} = render(YourComponent, {myProp: 'value'})
```
+The most common options you will need are:
+
+| Option | Description | Default |
+| --------- | ------------------------------------------------------- | ----------------------------------- |
+| `props` | Props to pass to the component. | N/A |
+| `context` | A `Map` of context values to render the component with. | N/A |
+| `target` | An `HTMLElement` to render the component into. | `
` appended to `document.body` |
+
+If you specify the `target` option, the `target` element will _not_ be appended
+to `document.body` automatically, and it will be used as the base element for
+[bound queries](#queries) and [`debug`](#debug).
+
+Refer to the [Svelte client-side component API docs][svelte-component-api] for
+all available options.
+
+[svelte-component-api]: https://svelte.dev/docs/client-side-component-api
+
### Render Options
-| Option | Description | Default |
-| ----------- | ----------------------------------------------------------------------------------- | --------------- |
-| `container` | The HTML element the component is mounted into. | `document.body` |
-| `queries` | Queries to bind to the container. See [within](dom-testing-library/api-within.mdx). | `null` |
+You may also customize how Svelte Testing Library renders your component. Most
+of the time, you shouldn't need to modify these options.
+
+:::caution
+
+Prior to `@testing-library/svelte@5.0.0`, the `baseElement` option was named
+`container`.
+
+:::
+
+| Option | Description | Default |
+| ------------- | --------------------------------------------------- | ------------------------------------------ |
+| `baseElement` | The base element for queries and [`debug`](#debug). | `componentOptions.target ?? document.body` |
+| `queries` | [Custom Queries][custom-queries]. | N/A |
+
+[custom-queries]: ../dom-testing-library/api-custom-queries.mdx
+
+### Render Results
+
+| Result | Description |
+| ----------------------------- | ---------------------------------------------------------- |
+| [`baseElement`](#baseelement) | The base DOM element used for queries. |
+| [`component`](#component) | The mounted Svelte component. |
+| [`container`](#container) | The DOM element the component is mounted to. |
+| [`debug`](#debug) | Log elements using [`prettyDOM`][pretty-dom]. |
+| [`rerender`](#rerender) | Update the component's props. |
+| [`unmount`](#unmount) | Unmount and destroy the component. |
+| [`...queries`](#queries) | [Query functions][query-functions] bound to `baseElement`. |
+
+[pretty-dom]: ../dom-testing-library/api-debugging.mdx#prettydom
+[query-functions]: ../queries/about.mdx
+
+#### `baseElement`
+
+_Added in `@testing-library/svelte@5.0.0`_
+
+The base DOM element that queries are bound to. Corresponds to
+`renderOptions.baseElement`. If you do not use `componentOptions.target` nor
+`renderOptions.baseElement`, this will be `document.body`.
+
+#### `container`
+
+The DOM element the component is mounted in. Corresponds to
+`componentOptions.target`. In general, avoid using `container` directly to query
+for elements; use [testing-library queries][query-functions] instead.
+
+:::tip
+
+Use `container.firstChild` to get the first rendered element of your component.
+
+:::
+
+:::caution
+
+Prior to `@testing-library/svelte@5.0.0`, `container` was set to the base
+element. Use `container.firstChild.firstChild` to get the first rendered element
+of the component in earlier versions.
+
+:::
+
+#### `component`
+
+The Svelte component instance. See the [Svelte component
+API][svelte-component-api] for more details.
+
+:::tip
+
+Avoid using `component` except to test developer-facing APIs, like exported
+functions. Instead, interact with the DOM. Read [Avoid the Test User][test-user]
+by Kent C. Dodds to understand the difference between the **end user** and
+**developer user**.
+
+:::
+
+[test-user]: https://kentcdodds.com/blog/avoid-the-test-user
+
+#### `debug`
+
+Log the `baseElement` or a given element using [`prettyDOM`][pretty-dom].
+
+:::tip
+
+If your `baseElement` is the default `document.body`, we recommend using
+[`screen.debug`][screen-debug].
+
+:::
+
+```js
+import {render, screen} from '@testing-library/svelte'
+
+const {debug} = render(MyComponent, {myProp: 'value'})
+
+const button = screen.getByRole('button')
-### Results
+// log `document.body`
+screen.debug()
-| Result | Description |
-| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `container` | The HTML element the component is mounted into. |
-| `component` | The newly created Svelte component. Generally, this should only be used when testing exported functions, or when you're testing developer facing API's. Outside of said cases avoid using the component directly to build tests, instead of interacting with the rendered Svelte component, work with the DOM. Have a read of [Avoid the Test User](https://kentcdodds.com/blog/avoid-the-test-user) by Kent C. Dodds to understand the difference between the **end user** and **developer user**. |
-| `debug` | Logs the `container` using [prettyDom](dom-testing-library/api-debugging.mdx/#prettydom). |
-| `unmount` | Unmounts the component from the `target` by calling `component.$destroy()`. |
-| `rerender` | Calls render again destroying the old component, and mounting the new component on the original `target` with any new options passed in. |
-| `...queries` | Returns all [query functions](queries/about.mdx) that are bound to the `container`. If you pass in `query` arguments than this will be those, otherwise all. |
+// log your custom `target` or `baseElement`
+debug()
+
+// log a specific element
+screen.debug(button)
+debug(button)
+```
+
+[screen-debug]: ../dom-testing-library/api-debugging.mdx#screendebug
+
+#### `rerender`
+
+Update one or more of the component's props and wait for Svelte to update.
+
+```js
+const {rerender} = render(MyComponent, {myProp: 'value'})
+
+await rerender({myProp: 'new value'}))
+```
+
+:::caution
+
+Prior to `@testing-library/svelte@5.0.0`, calling `rerender` would destroy and
+remount the component. Use `component.$set` and [`act`](#act) to update props in
+earlier versions:
+
+```js
+const {component} = render(MyComponent, {myProp: 'value'})
+
+await act(() => component.$set({myProp: 'new value'}))
+```
+
+:::
+
+#### `unmount`
+
+Unmount and destroy the Svelte component.
+
+```js
+const {unmount} = render(MyComponent, {myProp: 'value'})
+
+unmount()
+```
+
+#### Queries
+
+[Query functions][query-functions] bound to the [`baseElement`](#baseelement).
+If you passed [custom queries][custom-queries] to `render`, those will be
+available instead of the default queries.
+
+:::tip
+
+If your [`baseElement`](#baseelement) is the default `document.body`, we
+recommend using [`screen`][screen] rather than bound queries.
+
+:::
+
+```js
+import {render, screen} from '@testing-library/svelte'
+
+const {getByRole} = render(MyComponent, {myProp: 'value'})
+
+// query `document.body`
+const button = screen.getByRole('button')
+
+// query using a custom `target` or `baseElement`
+const button = getByRole('button')
+```
+
+[screen]: ../queries/about.mdx#screen
## `cleanup`
-> You don't need to import or use this, it's done automagically for you!
+Destroy all components and remove any elements added to `document.body`.
+
+:::info
+
+`cleanup` is called automatically if your testing framework adds a global
+`afterEach` method (e.g. Mocha, Jest, or Jasmine), or if you use
+`import '@testing-library/svelte/vitest'` in your [Vitest setup
+file][vitest-setup]. Usually, you shouldn't need to call `cleanup` yourself.
-Unmounts the component from the container and destroys the container.
+If you'd like to disable automatic cleanup in a framework that uses a global
+`afterEach` method, set `process.env.STL_SKIP_AUTO_CLEANUP`.
-📝 When you import anything from the library, this automatically runs after each
-test. If you'd like to disable this then set `process.env.STL_SKIP_AUTO_CLEANUP`
-to true or import `dont-clean-up-after-each` from the library.
+:::
```js
import {render, cleanup} from '@testing-library/svelte'
+// Default: runs after each test
afterEach(() => {
cleanup()
-}) // Default on import: runs it after each test.
+})
render(YourComponent)
-cleanup() // Or like this for more control.
+// Called manually for more control
+cleanup()
```
-## `act` (`async`)
-
-An `async` helper method that takes in a `function` or `Promise` that is
-immediately called/resolved, and then calls [`tick`][svelte-tick] so all pending
-state changes are flushed, and the view now reflects any changes made to the
-DOM.
+[vitest-setup]: ./setup.mdx#vitest
-## `fireEvent` (`async`)
+## `act`
-Calls `@testing-library/dom` [fireEvent](dom-testing-library/api-events.mdx). It
-is an `async` method due to calling [`tick`][svelte-tick] which tells Svelte to
-flush all pending state changes, basically it updates the DOM to reflect the new
-changes.
+Ensure all pending updates from Svelte are applied to the DOM. Optionally, you
+may pass a function to be called before updates are applied. If the function
+returns a `Promise`, it will be resolved first.
-**Component**
+Uses Svelte's [`tick`][svelte-tick] method to apply updates.
-```html
-
+const {component} = render(MyComponent)
-
+await act(() => {
+ component.updateSomething()
+})
```
-**Test**
+[svelte-tick]: https://svelte.dev/docs/svelte#tick
-```js
-import '@testing-library/jest-dom'
+## `fireEvent` (async)
-import {render, fireEvent, screen} from '@testing-library/svelte'
+Fire an event and wait for Svelte to update the DOM. An asynchronous wrapper of
+DOM Testing Library's [`fireEvent`][fire-event].
-import Comp from '..'
+:::tip
-test('count increments when button is clicked', async () => {
- render(Comp)
- const button = screen.getByText('Count is 0')
+Where possible, we recommend [`@testing-library/user-event`][user-event] instead
+of `fireEvent`.
- // Option 1.
- await fireEvent.click(button)
- expect(button).toHaveTextContent('Count is 1')
+:::
- // Option 2.
- await fireEvent(
- button,
- new MouseEvent('click', {
- bubbles: true,
- cancelable: true,
- }),
- )
- expect(button).toHaveTextContent('Count is 2')
-})
+```js
+import {fireEvent, render, screen} from '@testing-library/svelte'
+
+render(MyComponent)
+
+const button = screen.getByRole('button')
+await fireEvent.click(button)
```
-[svelte-tick]: https://svelte.dev/docs/svelte#tick
+[fire-event]: ../dom-testing-library/api-events.mdx
+[user-event]: ../user-event/intro.mdx
diff --git a/docs/svelte-testing-library/example.mdx b/docs/svelte-testing-library/example.mdx
index 26c8fe34a..3c9a08777 100644
--- a/docs/svelte-testing-library/example.mdx
+++ b/docs/svelte-testing-library/example.mdx
@@ -4,53 +4,77 @@ title: Example
sidebar_label: Example
---
-## Component
+:::tip
-```html
+See the [svelte-testing-library repository][repository-examples] for detailed
+examples, including examples for testing older Svelte 3 and 4 components.
+
+:::
+
+This basic example demonstrates how to:
+
+- Pass props to your Svelte component using [`render()`][render]
+- [Query][] the structure of your component's DOM elements using
+ [`screen`][screen]
+- Interact with your component using [`@testing-library/user-event`][user-event]
+- Make assertions using `expect`, using matchers from
+ [`@testing-library/jest-dom`][jest-dom]
+
+For additional resources, patterns, and best practices about testing Svelte
+components and other Svelte features, take a look at the [Svelte Society testing
+recipes][testing-recipes].
+
+```html title="basic.svelte"
-
Hello {name}!
+
-
+{#if showGreeting}
+
Hello {name}
+{/if}
```
-## Test
+```js title="basic.test.js"
+import {render, screen} from '@testing-library/svelte'
+import {userEvent} from '@testing-library/user-event'
+import {expect, test} from 'vitest'
-```js
-// NOTE: jest-dom adds handy assertions to Jest (and Vitest) and it is recommended, but not required.
-import '@testing-library/jest-dom'
+import Subject from './basic.svelte'
-import {render, fireEvent, screen} from '@testing-library/svelte'
+test('no initial greeting', () => {
+ render(Subject, {name: 'World'})
-import Comp from '../Comp'
+ const button = screen.getByRole('button', {name: 'Greet'})
+ const greeting = screen.queryByText(/hello/iu)
-test('shows proper heading when rendered', () => {
- render(Comp, {name: 'World'})
- const heading = screen.getByText('Hello World!')
- expect(heading).toBeInTheDocument()
+ expect(button).toBeInTheDocument()
+ expect(greeting).not.toBeInTheDocument()
})
-// Note: This is as an async test as we are using `fireEvent`
-test('changes button text on click', async () => {
- render(Comp, {name: 'World'})
- const button = screen.getByRole('button')
+test('greeting appears on click', async () => {
+ const user = userEvent.setup()
+ render(Subject, {name: 'World'})
- // Using await when firing events is unique to the svelte testing library because
- // we have to wait for the next `tick` so that Svelte flushes all pending state changes.
- await fireEvent.click(button)
+ const button = screen.getByRole('button')
+ await user.click(button)
+ const greeting = screen.getByText(/hello world/iu)
- expect(button).toHaveTextContent('Button Clicked')
+ expect(greeting).toBeInTheDocument()
})
```
-For additional resources, patterns and best practices about testing svelte
-components and other svelte features take a look at the
-[Svelte Society testing recipe](https://sveltesociety.dev/recipes/testing-and-debugging/unit-testing-svelte-component).
+[repository-examples]:
+ https://github.com/testing-library/svelte-testing-library/tree/main/examples
+[testing-recipes]:
+ https://sveltesociety.dev/recipes/testing-and-debugging/unit-testing-svelte-component
+[query]: ../queries/about.mdx
+[screen]: ../queries/about.mdx#screen
+[render]: ./api.mdx#render
+[user-event]: ../user-event/intro.mdx
+[jest-dom]: https://github.com/testing-library/jest-dom
diff --git a/docs/svelte-testing-library/faq.mdx b/docs/svelte-testing-library/faq.mdx
index bd2ab5dbe..764fd1a9b 100644
--- a/docs/svelte-testing-library/faq.mdx
+++ b/docs/svelte-testing-library/faq.mdx
@@ -5,22 +5,97 @@ sidebar_label: FAQ
---
- [Does this library also work with SvelteKit?](#does-this-library-also-work-with-sveltekit)
-- [Testing file upload component](#testing-file-upload-component)
+- [Why isn't `onMount` called when rendering components?](#why-isnt-onmount-called-when-rendering-components)
+- [How do I test file upload?](#how-do-i-test-file-upload)
+- [Why aren't transition events running?](#why-arent-transition-events-running)
---
## Does this library also work with SvelteKit?
-Yes, it does. It requires the same [setup](setup.mdx) as a "normal" Svelte
-project.
+Yes, it does. It requires the same [setup][] as a "normal" Svelte project.
-## Testing file upload component
+## Why isn't `onMount` called when rendering components?
-File upload handler not triggering? Use `happy-dom`, not `jsdom`, and make sure
-to use `fireEvent.change(...)` and not `fireEvent.input(...)`. It seems that
-jsdom (which is at v22.1.0 at the time of this writing) doesn't fake all the
-File object as it should.
+Since the test is running in a Node environment instead of a browser
+environment, it uses the SSR exports from Svelte, which declare all lifecycle
+events as no-ops.
-See
-[svelte-testing-library's issue 219](https://github.com/testing-library/svelte-testing-library/issues/219)
-for more details.
+One solution is to configure Vite to use browser resolutions in tests.
+
+```js title="vite.config.js"
+import {svelte} from '@sveltejs/vite-plugin-svelte'
+import {defineConfig} from 'vite'
+
+export default defineConfig(({mode}) => ({
+ plugins: [svelte()],
+ resolve: {
+ // The default would be [ 'svelte', 'node' ]
+ // as set by vite-plugin-svelte and vitest.
+ // This sets [ 'browser', 'svelte', 'node' ]
+ conditions: mode === 'test' ? ['browser'] : [],
+ },
+ test: {
+ environment: 'jsdom',
+ },
+}))
+```
+
+See svelte-testing-library's [issue 222][] for more details.
+
+[setup]: ./setup.mdx
+[issue 222]:
+ https://github.com/testing-library/svelte-testing-library/issues/222
+
+## How do I test file upload?
+
+Use the [upload][] utility from `@testing-library/user-event` rather than
+`fireEvent`. It works well in both [jsdom][] and [happy-dom][].
+
+```js
+test('upload file', async () => {
+ const user = userEvent.setup()
+
+ render(Uploader)
+ const file = new File(['hello'], 'hello.png', {type: 'image/png'})
+ const input = screen.getByLabelText(/upload file/i)
+
+ await user.upload(input, file)
+
+ expect(input.files[0]).toBe(file)
+ expect(input.files.item(0)).toBe(file)
+ expect(input.files).toHaveLength(1)
+})
+```
+
+[upload]: ../user-event/api-utility.mdx#upload
+[jsdom]: https://github.com/jsdom/jsdom
+[happy-dom]: https://github.com/capricorn86/happy-dom
+
+## Why aren't [transition events][] running?
+
+The [jsdom][] implementation of `requestAnimationFrame` can be unreliable in
+Vitest. To work around it, you can:
+
+- Switch to [happy-dom][], if you are able, which does not exhibit the same
+ issues
+- Replace the implementation of `requestAnimationFrame`
+
+```js
+beforeEach(() => {
+ const raf = fn => setTimeout(() => fn(new Date()), 16)
+ vi.stubGlobal('requestAnimationFrame', raf)
+})
+
+// Alternatively, set `unstubGlobals: true` in vitest.config.js
+afterEach(() => {
+ vi.unstubAllGlobals()
+})
+```
+
+See svelte-testing-library's [issue 206][] for more details.
+
+[transition events]:
+ https://svelte.dev/docs/element-directives#transition-events
+[issue 206]:
+ https://github.com/testing-library/svelte-testing-library/issues/206
diff --git a/docs/svelte-testing-library/intro.mdx b/docs/svelte-testing-library/intro.mdx
index 375ae98cd..4c0a5df1f 100644
--- a/docs/svelte-testing-library/intro.mdx
+++ b/docs/svelte-testing-library/intro.mdx
@@ -12,9 +12,10 @@ sidebar_label: Introduction
npm install --save-dev @testing-library/svelte
```
-> This library is built on top of
-> [`DOM Testing Library`](dom-testing-library/intro.mdx) which is where most of
-> the logic behind the queries is.
+> This library is built on top of [`dom-testing-library`][dom-testing-library]
+> which is where most of the logic behind the queries is.
+
+[dom-testing-library]: ../dom-testing-library/intro.mdx
## The Problem
@@ -27,14 +28,16 @@ The Svelte Testing Library is a very lightweight solution for testing Svelte
components. It provides light utility functions on top of `svelte`, in a way
that encourages better testing practices. Its primary guiding principle is:
-> [The more your tests resemble the way your software is used, the more confidence they can give you.](https://twitter.com/kentcdodds/status/977018512689455106)
+> [The more your tests resemble the way your software is used, the more
+> confidence they can give you.][testing-library-blurb]
So rather than dealing with instances of rendered Svelte components, your tests
-will work with actual DOM nodes. See the [Dom
-Introduction][dom-solution-explainer] for a more in-depth explanation.
+will work with actual DOM nodes. See the
+[`dom-testing-library`][dom-solution-explainer] for a more in-depth explanation.
+[testing-library-blurb]:
+ https://twitter.com/kentcdodds/status/977018512689455106
[dom-solution-explainer]: ../dom-testing-library/intro.mdx#this-solution
-[react-solution-explainer]: ../react-testing-library/intro.mdx#this-solution
**What this library is not**:
diff --git a/docs/svelte-testing-library/setup.mdx b/docs/svelte-testing-library/setup.mdx
index 4eff0ad06..849335f21 100644
--- a/docs/svelte-testing-library/setup.mdx
+++ b/docs/svelte-testing-library/setup.mdx
@@ -4,192 +4,257 @@ title: Setup
sidebar_label: Setup
---
-We recommend using [Vitest](https://vitest.dev/) but you're free to use the
-library with any testing framework and runner you're comfortable with.
+We recommend using [Vitest][], but you're free to use the library with any test
+runner that's ESM compatible.
-## Vitest
-
-1. Install Vitest and jsdom
+[vitest]: https://vitest.dev/
-We're using `jsdom` here as the test environment, but you can use any other
-options e.g `happy-dom`.
+## Vitest
-```bash npm2yarn
-npm install --save-dev vitest jsdom
-```
+1. Add development dependencies
-Optionally install `@vitest/ui`, which opens a UI within a browser window to
-follow the progress and interact with your tests.
+ - [`@testing-library/svelte`][@testing-library/svelte]
+ - [`@testing-library/jest-dom`][@testing-library/jest-dom] (Optional, but
+ highly recommended)
+ - [`@sveltejs/vite-plugin-svelte`][@sveltejs/vite-plugin-svelte]
+ - [`vitest`][vitest]
+ - [`jsdom`][jsdom], [`happy-dom`][happy-dom], or other [Vitest DOM
+ environment][vitest dom]
-```bash npm2yarn
-npm install --save-dev @vitest/ui
-```
+ ```bash npm2yarn
+ npm install --save-dev \
+ @testing-library/svelte \
+ @testing-library/jest-dom \
+ @sveltejs/vite-plugin-svelte \
+ vitest \
+ jsdom
+ ```
-1. Add the test scipts to your `package.json` to run the tests with Vitest
+ Optionally install [`@vitest/ui`][@vitest/ui], which opens a UI within a
+ browser window to follow the progress and interact with your tests.
- ```json
- {
- "scripts": {
- "test": "vitest run",
- "test:ui": "vitest --ui",
- "test:watch": "vitest"
- }
- }
+ ```bash npm2yarn
+ npm install --save-dev @vitest/ui
```
-2. To compile the Svelte components before using them in Vitest, you need to
- install
- [@sveltejs/vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte)
- and Vite
+2. Add a `vitest-setup.js` file to optionally set up
+ [`@testing-library/jest-dom`][@testing-library/jest-dom] expect matchers.
- ```bash npm2yarn
- npm install --save-dev @sveltejs/vite-plugin-svelte vite
+ ```js title="vitest-setup.js"
+ import '@testing-library/jest-dom/vitest'
```
-3. Add a `vitest.config.ts` configuration file to the root of your project
+3. Add `vitest.config.js`, or update your existing `vite.config.js`, with the
+ `svelte` and `svelteTesting` Vite plugins. Set the testing environment to
+ your DOM library of choice and optionally configure your setup file from step
+ (2).
- ```js
+ ```js title="vite.config.js"
import {defineConfig} from 'vitest/config'
import {svelte} from '@sveltejs/vite-plugin-svelte'
+ import {svelteTesting} from '@testing-library/svelte/vite'
export default defineConfig({
- plugins: [svelte({hot: !process.env.VITEST})],
+ plugins: [svelte(), svelteTesting()],
test: {
- globals: true,
environment: 'jsdom',
+ setupFiles: ['./vitest-setup.js'],
},
})
```
-4. Optionally install [vitest-dom](https://github.com/chaance/vitest-dom) to add
- handy assertions to Vitest
+ Or, if you're using [SvelteKit][sveltekit]:
- 4.1 Install `vitest-dom`
+ ```js title="vite.config.js"
+ import {defineConfig} from 'vitest/config'
+ import {sveltekit} from '@sveltejs/kit/vite'
+ import {svelteTesting} from '@testing-library/svelte/vite'
- ```bash npm2yarn
- npm install --save-dev vitest-dom
+ export default defineConfig({
+ plugins: [sveltekit(), svelteTesting()],
+ test: {
+ environment: 'jsdom',
+ setupFiles: ['./vitest-setup.js'],
+ },
+ })
```
- 4.2 import `vitest-dom` at within the vitest setup file (usually `vitest-setup.(js|ts)`)
+ :::note
+
+ The `svelteTesting` plugin:
+
+ - Adds an automatic cleanup fixture to [`test.setupFiles`][test-setup-files]
+ - Adds `browser` to [`resolve.conditions`][resolve-conditions]
+
+ If needed, you can disable either behavior. Disabling both options is
+ equivalent to omitting the plugin.
```js
- import * as matchers from "vitest-dom/matchers";
- import { expect } from "vitest";
- expect.extend(matchers);
-
- // or:
- import "vitest-dom/extend-expect";
+ svelteTesting({
+ // disable auto cleanup
+ autoCleanup: false,
+ // disable browser resolution condition
+ resolveBrowser: false,
+ })
+ ```
+
+ Resolving the `browser` condition may cause issues if you have a complex Vite
+ configuration or dependencies that cannot be loaded into Node.js. See
+ [testing-library/svelte-testing-library#222][] for more information and
+ alternative configuration options to ensure Svelte's browser code is used.
+ :::
+
+4. Add test scripts to your `package.json` to run the tests with Vitest
+
+ ```json title="package.json"
+ {
+ "scripts": {
+ "test": "vitest run",
+ "test:ui": "vitest --ui",
+ "test:watch": "vitest"
+ }
+ }
```
5. Create your component and a test file (checkout the rest of the docs to see
how) and run the following command to run the tests.
```bash npm2yarn
- npm run test
+ npm test
```
-## Jest
-
-1. Install Jest & jest-environment-jsdom
-
- ```bash npm2yarn
- npm install --save-dev jest jest-environment-jsdom
- ```
-
-2. Add the following to your `package.json`
+[@testing-library/svelte]:
+ https://github.com/testing-library/svelte-testing-library
+[@testing-library/jest-dom]: https://github.com/testing-library/jest-dom
+[@sveltejs/vite-plugin-svelte]: https://github.com/sveltejs/vite-plugin-svelte
+[jsdom]: https://github.com/jsdom/jsdom
+[happy-dom]: https://github.com/capricorn86/happy-dom
+[@vitest/ui]: https://vitest.dev/guide/ui.html
+[vitest dom]: https://vitest.dev/guide/environment.html
+[sveltekit]: https://kit.svelte.dev/
+[testing-library/svelte-testing-library#222]:
+ https://github.com/testing-library/svelte-testing-library/issues/222
+[test-setup-files]: https://vitest.dev/config/#setupfiles
+[resolve-conditions]:
+ https://vitejs.dev/config/shared-options.html#resolve-conditions
- ```json
- {
- "scripts": {
- "test": "jest src",
- "test:watch": "npm run test -- --watch"
- }
- }
- ```
+### TypeScript
-3. You'll need to compile the Svelte components before using them in Jest, so
- we need to install
- [svelte-jester](https://github.com/mihar-22/svelte-jester)
+Include [`@testing-library/jest-dom`][@testing-library/jest-dom] to the TypeScript `types` to make the TypeScript compiler aware about the [`@testing-library/jest-dom`][@testing-library/jest-dom] matchers.
- ```bash npm2yarn
- npm install --save-dev svelte-jester
- ```
+```json title="tsconfig.json"
+{
+ "compilerOptions": {
+ "types": ["@testing-library/jest-dom"],
+ }
+}
+```
-4. Add the following Jest configuration to your `package.json`
+## Jest
- ```json
- {
- "jest": {
- "transform": {
- "^.+\\.svelte$": "svelte-jester"
- },
- "moduleFileExtensions": ["js", "svelte"],
- "testEnvironment": "jsdom"
- }
- }
- ```
+[`@testing-library/svelte`][@testing-library/svelte] is ESM-only, which means
+you must use Jest in [ESM mode][jest esm mode].
-5. If you are using ES6 modules in your project you have to add Jest's babel
- transform setting (it is set by default, but since we are overriding the
- transform config, we have to add it explicitly)
+1. Add development dependencies
- 5.1 Install `babel-jest`
+ - [`@testing-library/svelte`][@testing-library/svelte]
+ - [`@testing-library/jest-dom`][@testing-library/jest-dom] (Optional, but
+ highly recommended)
+ - [`svelte-jester`][svelte-jester]
+ - [`jest`][vitest]
+ - [`jest-environment-jsdom`][jest-environment-jsdom]
- ```bash npm2yarn
- npm install --save-dev babel-jest
- ```
+ ```bash npm2yarn
+ npm install --save-dev \
+ @testing-library/svelte \
+ @testing-library/jest-dom \
+ svelte-jester \
+ jest \
+ jest-environment-jsdom
+ ```
- 5.2. Add a basic `.babelrc` configuration
+2. Add a `jest-setup.js` file to configure
+ [`@testing-library/jest-dom`][@testing-library/jest-dom], if using.
- ```json
- {
- "presets": [["@babel/preset-env", {"targets": {"node": "current"}}]]
- }
- ```
+ ```ts title="jest-setup.js"
+ import '@testing-library/jest-dom'
+ ```
- 5.3. Update the Jest transform configuration
+3. Configure Jest to use jsdom, transform Svelte files, and use your setup file
- ```json
- "transform": {
- "^.+\\.js$": "babel-jest",
- "^.+\\.svelte$": "svelte-jester"
- },
- ```
+ ```js title="jest.config.js"
+ export default {
+ transform: {
+ '^.+\\.svelte$': 'svelte-jester',
+ },
+ moduleFileExtensions: ['js', 'svelte'],
+ extensionsToTreatAsEsm: ['.svelte'],
+ testEnvironment: 'jsdom',
+ setupFilesAfterEnv: ['/jest-setup.js'],
+ }
+ ```
-6. This is optional but it is recommended, you can install
- [jest-dom](https://github.com/testing-library/jest-dom) to add handy
- assertions to Jest
+ :::note
+
+ If you are using Svelte 5, you must use `svelte-jester@5` or later, and you
+ will need to make additional changes to your Jest configuration.
+
+ - Update `transform` to compile `.svelte.(js|ts)` modules
+ - Allow `@testing-library/svelte` to be transformed, even though it's in
+ `node_modules`
+
+ ```diff title="jest.config.js"
+ export default {
+ transform: {
+ - '^.+\\.svelte$': 'svelte-jester',
+ + '^.+\\.svelte(\\.(js|ts))?$': 'svelte-jester',
+ },
+ + transformIgnorePatterns: [
+ + '/node_modules/(?!@testing-library/svelte/)',
+ + ],
+ moduleFileExtensions: ['js', 'svelte'],
+ extensionsToTreatAsEsm: ['.svelte'],
+ testEnvironment: 'jsdom',
+ setupFilesAfterEnv: ['/jest-setup.js'],
+ }
+ ```
- 6.1 Install `jest-dom`
+ :::
- ```bash npm2yarn
- npm install --save-dev @testing-library/jest-dom
- ```
+4. Add the following to your `package.json`
- 6.2 Add the following to your Jest configuration in `package.json`
+ ```json title="package.json"
+ {
+ "scripts": {
+ "test": "npx --node-options=\"--experimental-vm-modules\" jest src",
+ "test:watch": "npx --node-options=\"--experimental-vm-modules\" jest src --watch"
+ }
+ }
+ ```
- ```json
- {
- "setupFilesAfterEnv": ["@testing-library/jest-dom/extend-expect"]
- }
- ```
+5. Create your component + test file (checkout the rest of the docs to see how)
+ and run it
-7. Create your component + test file (checkout the rest of the docs to see how)
- and run it
+ ```bash npm2yarn
+ npm test
+ ```
- ```bash npm2yarn
- npm run test
- ```
+[jest esm mode]: https://jestjs.io/docs/ecmascript-modules
+[svelte-jester]: https://github.com/svelteness/svelte-jester
+[jest-environment-jsdom]:
+ https://jestjs.io/docs/configuration#testenvironment-string
-### TypeScript
+### TypeScript and preprocessors
To use TypeScript with Jest, you'll need to install and configure
`svelte-preprocess` and `ts-jest`. For full instructions, see the
-[`svelte-jester`](https://github.com/mihar-22/svelte-jester#typescript) docs.
+[`svelte-jester` docs][svelte-jester typescript].
-### Preprocessors
+If you'd like include any other [Svelte preprocessors][svelte-preprocess], see
+the [`svelte-jester` docs][svelte-jester preprocess].
-If you'd like to also include any
-[Svelte preprocessors](https://github.com/sveltejs/svelte-preprocess) then
-simply follow the instructions over at
-[svelte-jester](https://github.com/mihar-22/svelte-jester#babel).
+[svelte-preprocess]: https://github.com/sveltejs/svelte-preprocess
+[svelte-jester typescript]:
+ https://github.com/svelteness/svelte-jester#typescript
+[svelte-jester preprocess]:
+ https://github.com/svelteness/svelte-jester#preprocess
diff --git a/docs/user-event/api-clipboard.mdx b/docs/user-event/api-clipboard.mdx
index 32847ddf9..10c935213 100644
--- a/docs/user-event/api-clipboard.mdx
+++ b/docs/user-event/api-clipboard.mdx
@@ -7,7 +7,8 @@ Note that the
[Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) is
usually not available outside of secure context.
To enable testing of workflows involving the clipboard,
-[`userEvent.setup()`](setup.mdx) replaces `window.navigator.clipboard` with a stub.
+[`userEvent.setup()`](setup.mdx) replaces `window.navigator.clipboard` with a
+stub.
## copy()
@@ -45,5 +46,5 @@ Paste data into the document.
When called without `clipboardData`, the content to be pasted is read from the
`Clipboard`.
-When performed in editable context, the pasted content is inserted into to the
+When performed in editable context, the pasted content is inserted into the
document.
diff --git a/docs/user-event/api-convenience.mdx b/docs/user-event/api-convenience.mdx
index aa2876ce1..caf24ffee 100644
--- a/docs/user-event/api-convenience.mdx
+++ b/docs/user-event/api-convenience.mdx
@@ -191,8 +191,9 @@ test('tab', async () => {
// cycle goes back to the body element
expect(document.body).toHaveFocus()
- await user.tab()
+ // simulate Shift-Tab
+ await user.tab({shift: true})
- expect(checkbox).toHaveFocus()
+ expect(number).toHaveFocus()
})
```
diff --git a/docs/user-event/api-keyboard.mdx b/docs/user-event/api-keyboard.mdx
index 96e8dcd41..b3bc098b4 100644
--- a/docs/user-event/api-keyboard.mdx
+++ b/docs/user-event/api-keyboard.mdx
@@ -16,8 +16,8 @@ Keystrokes can be described:
```js
keyboard('foo') // translates to: f, o, o
```
- The opening brackets `{` and `[` are used as special characters and can be referenced
- by doubling them.
+ The opening brackets `{` and `[` are used as special characters and can be
+ referenced by doubling them.
```js
keyboard('{{a[[') // translates to: {, a, [
```
diff --git a/docs/user-event/api-pointer.mdx b/docs/user-event/api-pointer.mdx
index f4fff24f8..3c2d93264 100644
--- a/docs/user-event/api-pointer.mdx
+++ b/docs/user-event/api-pointer.mdx
@@ -14,11 +14,11 @@ accepts a single pointer action or an array of them.
type PointerInput = PointerActionInput | Array
```
-> Our primary target audience tests per `jest` in a `jsdom` environment and there
-is no layout in `jsdom`. This means that different from your browser the
-elements don't exist in a specific position, layer and size.
-We don't try to determine if the pointer action you describe is possible at that
-position in your layout.
+> Our primary target audience tests per `jest` in a `jsdom` environment and
+> there is no layout in `jsdom`. This means that different from your browser the
+> elements don't exist in a specific position, layer and size.
+> We don't try to determine if the pointer action you describe is possible at
+> that position in your layout.
## Pointer action
@@ -43,8 +43,8 @@ pointer({keys: '[MouseLeft][MouseRight]'})
pointer('[MouseLeft][MouseRight]')
```
-In order to press a button without releasing it, the button name is suffixed with
-`>`.
+In order to press a button without releasing it, the button name is suffixed
+with `>`.
For just releasing a previously pressed button, the tag is started with `/`.
```js
@@ -52,7 +52,8 @@ pointer('[MouseLeft>]') // press the left mouse button
pointer('[/MouseLeft]') // release the left mouse button
```
-Which buttons are available depends on the [`pointerMap`](options.mdx#pointermap).
+Which buttons are available depends on the
+[`pointerMap`](options.mdx#pointermap).
### Moving a pointer
@@ -127,10 +128,7 @@ position to be used for any selection.
pointer({target: element, offset: 2, keys: '[MouseLeft]'})
// =>
pointer({target: element, node: element, offset: 1, keys: '[MouseLeft]'})
diff --git a/docs/user-event/api-utility.mdx b/docs/user-event/api-utility.mdx
index 380e891c6..39805e3f7 100644
--- a/docs/user-event/api-utility.mdx
+++ b/docs/user-event/api-utility.mdx
@@ -121,8 +121,8 @@ type(
Type into an input element.
-> You should use [`keyboard()`](api-keyboard.mdx) if you want to just simulate pressing
-> buttons on the keyboard.
+> You should use [`keyboard()`](api-keyboard.mdx) if you want to just simulate
+> pressing buttons on the keyboard.
> You can use `type()` if you just want to conveniently insert some text into an
> input field or textarea.
diff --git a/docs/user-event/install.mdx b/docs/user-event/install.mdx
index db2be35f6..9e92a29b0 100644
--- a/docs/user-event/install.mdx
+++ b/docs/user-event/install.mdx
@@ -3,29 +3,10 @@ id: install
title: Installation
---
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
-
-
-
-
-```sh
+```bash npm2yarn
npm install --save-dev @testing-library/user-event
```
-
-
-
-```sh
-yarn add --dev @testing-library/user-event
-```
-
-
-
-
Note that `@testing-library/user-event` requires `@testing-library/dom`.
If you use one of the
diff --git a/docs/user-event/intro.mdx b/docs/user-event/intro.mdx
index 8d6a31e31..f4319fec1 100644
--- a/docs/user-event/intro.mdx
+++ b/docs/user-event/intro.mdx
@@ -11,7 +11,7 @@ events that would happen if the interaction took place in a browser.
These docs describe `user-event@14`. We recommend updating your projects to this
version, as it includes important bug fixes and new features. You can find the
-docs for `user-event@13.5.0` [here](../ecosystem-user-event.mdx), and the
+docs for `user-event@13.5.0` [here](./v13.mdx), and the
changelog for the release
[here](https://github.com/testing-library/user-event/releases/tag/v14.0.0).
@@ -42,13 +42,18 @@ factors in that the browser e.g. wouldn't let a user click a hidden element or
type in a disabled text box.
This is
[why you should use `user-event`](https://ph-fritsche.github.io/blog/post/why-userevent)
-to test interaction with your components.
+to test interaction with your components.
-There are, however, some user interactions or aspects of these [that aren't yet implemented and thus can't yet be described with `user-event`](https://github.com/testing-library/user-event/issues?q=is%3Aopen+label%3Aaccuracy%2Cenhancement).
-In these cases you can use `fireEvent` to dispatch the concrete events that your software relies on.
+There are, however, some user interactions or aspects of these
+[that aren't yet implemented and thus can't yet be described with `user-event`](https://github.com/testing-library/user-event/issues?q=is%3Aopen+label%3Aaccuracy%2Cenhancement).
+In these cases you can use `fireEvent` to dispatch the concrete events that your
+software relies on.
-Note that this makes your component and/or test reliant upon your assumptions about the concrete aspects of the interaction being correct.
-Therefore if you already put in the work to specify the correct aspects of such interaction, please consider contributing to this project so that `user-event` might cover these cases too.
+Note that this makes your component and/or test reliant upon your assumptions
+about the concrete aspects of the interaction being correct. Therefore if you
+already put in the work to specify the correct aspects of such interaction,
+please consider contributing to this project so that `user-event` might cover
+these cases too.
## Writing tests with `userEvent`
@@ -68,7 +73,7 @@ test('trigger some awesome feature when clicking the button', async () => {
// See https://testing-library.com/docs/dom-testing-library/install#wrappers
render()
- await user.click(screen.getByRole('button', { name: /click me!/i }))
+ await user.click(screen.getByRole('button', {name: /click me!/i}))
// ...assertions...
})
@@ -88,13 +93,13 @@ function setup(jsx) {
}
test('render with a setup function', async () => {
- const { user } = setup()
+ const {user} = setup()
// ...
})
```
Note that, while directly invoking APIs such as `userEvent.click()` (which will
-trigger `setup` internally) is [still supported in
-v14](https://testing-library.com/docs/user-event/setup#direct-apis), this option
-exists to ease the migration from v13 to v14, and for simple tests. We recommend
-using the methods on the instances returned by `userEvent.setup()`.
+trigger `setup` internally) is
+[still supported in v14](https://testing-library.com/docs/user-event/setup#direct-apis),
+this option exists to ease the migration from v13 to v14, and for simple tests.
+We recommend using the methods on the instances returned by `userEvent.setup()`.
diff --git a/docs/user-event/options.mdx b/docs/user-event/options.mdx
index 6d4153560..014912680 100644
--- a/docs/user-event/options.mdx
+++ b/docs/user-event/options.mdx
@@ -8,8 +8,21 @@ can be applied per [`setup()`](setup.mdx).
### advanceTimers
-If you are using fake timers, you need to advance your timers when we internally
-[delay](#delay) subsequent code.
+`user-event` adds a [delay](#delay) between some subsequent inputs. When using
+[fake timers](/guides-using-fake-timers.mdx) it is necessary to set this option
+to your test runner's time advancement function. For example:
+
+```js
+const user = userEvent.setup({advanceTimers: jest.advanceTimersByTime})
+```
+
+:::caution
+
+You may find suggestions to set `delay: null` to prevent test timeouts when
+using fake timers. That is not recommended, as it may cause unexpected
+behaviour. Starting from v14.1, we suggest using `advanceTimers` option instead.
+
+:::
```ts
(delay: number) => Promise | void
@@ -38,7 +51,7 @@ The feature therefore will not constitute a breaking change.
### delay
Between some subsequent inputs like typing a series of characters the code
-execution is delayed per `setTimeout` for (at least) `delay` seconds.
+execution is delayed per `setTimeout` for (at least) `delay` milliseconds.
This moves the next changes at least to the next macro task and allows other
(asynchronous) code to run between events.
diff --git a/docs/user-event/setup.mdx b/docs/user-event/setup.mdx
index 77546ab4d..ee43d0f44 100644
--- a/docs/user-event/setup.mdx
+++ b/docs/user-event/setup.mdx
@@ -27,7 +27,7 @@ This allows to write multiple consecutive interactions that behave just like the
described interactions by a real user.
```js
-import userEvent from "@testing-library/user-event";
+import userEvent from '@testing-library/user-event'
const user = userEvent.setup()
@@ -41,7 +41,8 @@ instance that shares the same input device state.
The [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard)
is usually not available outside of secure context.
To enable testing of workflows involving the clipboard,
-[`userEvent.setup()`](setup.mdx) replaces `window.navigator.clipboard` with a stub.
+[`userEvent.setup()`](setup.mdx) replaces `window.navigator.clipboard` with a
+stub.
## Direct APIs
diff --git a/docs/ecosystem-user-event.mdx b/docs/user-event/v13.mdx
similarity index 97%
rename from docs/ecosystem-user-event.mdx
rename to docs/user-event/v13.mdx
index c715a584a..916391dc3 100644
--- a/docs/ecosystem-user-event.mdx
+++ b/docs/user-event/v13.mdx
@@ -1,11 +1,8 @@
---
-id: ecosystem-user-event
+id: v13
title: user-event v13
---
-import Tabs from '@theme/Tabs'
-import TabItem from '@theme/TabItem'
-
[`user-event`][gh] is a companion library for Testing Library that provides more
advanced simulation of browser interactions than the built-in
[`fireEvent`](dom-testing-library/api-events.mdx#fireevent) method.
@@ -21,23 +18,10 @@ fixes and new features.
## Installation
-
-
-
-```sh
+```bash npm2yarn
npm install --save-dev @testing-library/user-event @testing-library/dom
```
-
-
-
-```sh
-yarn add --dev @testing-library/user-event @testing-library/dom
-```
-
-
-
-
Now simply import it in your tests:
```js
@@ -615,22 +599,27 @@ method.
Usage example:
```jsx
-import React, { useState } from 'react'
+import React, {useState} from 'react'
import {render, screen} from '@testing-library/react'
import userEvent, {specialChars} from '@testing-library/user-event'
const InputElement = () => {
- const [currentValue, setCurrentValue] = useState('This is a bad example');
- return
-
- setCurrentValue(e.target.value)} />
-
;
+ const [currentValue, setCurrentValue] = useState('This is a bad example')
+ return (
+
+
+ setCurrentValue(e.target.value)}
+ />
+
+ )
}
test('delete characters within the selectedRange', () => {
- render(
- ,
- )
+ render()
const input = screen.getByLabelText(/example/i)
input.setSelectionRange(10, 13)
userEvent.type(input, `${specialChars.backspace}good`)
diff --git a/docs/vue-testing-library/api.mdx b/docs/vue-testing-library/api.mdx
index 3bac572ba..e9f3c591e 100644
--- a/docs/vue-testing-library/api.mdx
+++ b/docs/vue-testing-library/api.mdx
@@ -58,7 +58,7 @@ The valid Vue Component to be tested.
#### Options
An object containing additional information to be passed to `@vue/test-utils`
-[mount](https://vue-test-utils.vuejs.org/api/options.html#mounting-options).
+[mount](https://test-utils.vuejs.org/api/#mount).
Additionally, the following options can also be provided:
@@ -171,22 +171,21 @@ This is a simple wrapper around `prettyDOM` which is also exposed and comes from
#### `unmount()`
An alias for `@vue/test-utils`
-[destroy](https://vue-test-utils.vuejs.org/api/wrapper/#destroy).
+[unmount](https://test-utils.vuejs.org/api/#unmount).
#### `html()`
-An alias for `@vue/test-utils`
-[html](https://vue-test-utils.vuejs.org/api/wrapper/#html).
+An alias for `@vue/test-utils` [html](https://test-utils.vuejs.org/api/#html).
#### `emitted()`
An alias for `@vue/test-utils`
-[emitted](https://vue-test-utils.vuejs.org/api/wrapper/#emitted).
+[emitted](https://test-utils.vuejs.org/api/#emitted).
#### `rerender(props)`
An alias for `@vue/test-utils`
-[setProps](https://test-utils.vuejs.org/api/#setprops).
+[setProps](https://test-utils.vuejs.org/api/#setProps).
It returns a Promise through so you can `await rerender(...)`.
@@ -232,9 +231,9 @@ See a working example of `update` in the
Unmounts Vue trees that were mounted with
[render](#rendercomponent-options-callback).
-> If you are using an environment that supports `afterEach` hook (as in Jest),
-> there's no need to call `cleanup` manually. Vue Testing Library handles it for
-> you.
+> This is called automatically if your testing framework (such as mocha, Jest or
+> Jasmine) injects a global `afterEach()` function into the testing environment.
+> If not, you will need to call `cleanup()` after each test.
Failing to call `cleanup` when you've called `render` could result in a memory
leak and tests which are not idempotent (which can lead to difficult to debug
diff --git a/docs/vue-testing-library/examples.mdx b/docs/vue-testing-library/examples.mdx
index 677238545..e52eba3bc 100644
--- a/docs/vue-testing-library/examples.mdx
+++ b/docs/vue-testing-library/examples.mdx
@@ -1,10 +1,18 @@
---
id: examples
-title: Examples
+title: Example
---
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
## Basic example
+
+
+
+
```html
+
+
+
+```
+
+```ts
+import {render, fireEvent, screen} from '@testing-library/vue'
+import Component from './Component.vue'
+
+test('increments value on click', async () => {
+ render(Component)
+
+ // screen has all queries that you can use in your tests.
+ // getByText returns the first matching node for the provided text, and
+ // throws an error if no elements match or if more than one match is found.
+ screen.getByText('Times clicked: 0')
+
+ const button = screen.getByText('increment')
+
+ // Dispatch a native click event to our button element.
+ await fireEvent.click(button)
+ await fireEvent.click(button)
+
+ screen.getByText('Times clicked: 2')
+})
+```
+
+
+
+
+
## Example using `v-model`:
+
+
+
+
```html
Learn how to test JavaScript with{' '}
Kent C. Dodds
@@ -96,7 +98,7 @@ export default function Help(props) {
-
Want to help?
+
Want to help?
Thanks! The Testing Library maintainers are happy to maintain this
@@ -105,7 +107,7 @@ export default function Help(props) {
community successful and improve tests for everyone in the long
run.
-
+
Please consider helping us answer community questions and update
documentation content via the help links above. You can also help
support{' '}
diff --git a/src/pages/index.js b/src/pages/index.js
index 6f7611c77..c5ba1cc8a 100755
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -30,7 +30,12 @@ const HomeSplash = props => {
const Logo = props => (