Skip to content

Commit

Permalink
docs(svelte-testing-library): update API docs for v5 release (#1369)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored Apr 29, 2024
1 parent 882dff9 commit 288b86f
Showing 1 changed file with 247 additions and 90 deletions.
337 changes: 247 additions & 90 deletions docs/svelte-testing-library/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,149 +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 `<div>` appended to `document.body`.

```js
import {render} from '@testing-library/svelte'
import MyComponent from './MyComponent.svelte'

const view = 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 view = 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 view = 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. | `<div>` 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`

> 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.
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
<script>
let count = 0
```js
import {act, render} from '@testing-library/svelte'

function handleClick() {
count += 1
}
</script>
const {component} = render(MyComponent)

<button on:click="{handleClick}">Count is {count}</button>
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

0 comments on commit 288b86f

Please sign in to comment.