diff --git a/docs/svelte-testing-library/example.mdx b/docs/svelte-testing-library/example.mdx index 26c8fe34a..639e55d81 100644 --- a/docs/svelte-testing-library/example.mdx +++ b/docs/svelte-testing-library/example.mdx @@ -4,53 +4,275 @@ title: Example sidebar_label: Example --- -## Component +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 +[testing-recipes]: + https://sveltesociety.dev/recipes/testing-and-debugging/unit-testing-svelte-component + +## Basic + +This basic example demonstrates how to: + +- Pass props to your Svelte component using `render` +- Query the structure of your component's DOM elements using `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] + +```html title="greeter.svelte" -
Hello {name}
+{/if} ``` -## Test +```js title="greeter.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 Greeter from './greeter.svelte' -import {render, fireEvent, screen} from '@testing-library/svelte' +test('no initial greeting', () => { + render(Greeter, {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'}) +test('greeting appears on click', async () => { + const user = userEvent.setup() + render(Greeter, {name: 'World'}) + const button = screen.getByRole('button') + await user.click(button) + const greeting = screen.getByText(/hello world/iu) + + expect(greeting).toBeInTheDocument() +}) +``` + +[user-event]: ../user-event/intro.mdx +[jest-dom]: https://github.com/testing-library/jest-dom + +## Events + +Events can be tested using spy functions. If you're using Vitest you can use +[`vi.fn()`][vi-fn] to create a spy. + +:::info + +Consider using function props to make testing events easier. + +::: + +```html title="button-with-event.svelte" + +``` + +```html title="button-with-prop.svelte" + + + +``` + +```js title="button.test.ts" +import {render, screen} from '@testing-library/svelte' +import userEvent from '@testing-library/user-event' +import {expect, test, vi} from 'vitest' + +import ButtonWithEvent from './button-with-event.svelte' +import ButtonWithProp from './button-with-prop.svelte' + +test('button with event', async () => { + const user = userEvent.setup() + const onClick = vi.fn() + + const {component} = render(ButtonWithEvent) + component.$on('click', onClick) + + const button = screen.getByRole('button') + await user.click(button) + + expect(onClick).toHaveBeenCalledOnce() +}) + +test('button with function prop', async () => { + const user = userEvent.setup() + const onClick = vi.fn() + + render(ButtonWithProp, {onClick}) + + const button = screen.getByRole('button') + await user.click(button) + + expect(onClick).toHaveBeenCalledOnce() +}) +``` + +[vi-fn]: https://vitest.dev/api/vi.html#vi-fn + +## Slots + +Slots cannot be tested directly. It's usually easier to structure your code so +that you can test the user-facing results, leaving any slots as an +implementation detail. + +However, if slots are an important developer-facing API of your component, you +can use a wrapper component and "dummy" children to test them. Test IDs can be +helpful when testing slots in this manner. + +```html title="heading.svelte" +{message.text}
+