@@ -4,48 +4,51 @@ title: Example
4
4
sidebar_label : Example
5
5
---
6
6
7
- For additional resources, patterns, and best practices about testing Svelte
8
- components and other Svelte features, take a look at the [ Svelte Society testing
9
- recipes] [ testing-recipes ] .
7
+ :::tip
10
8
11
- [ testing-recipes ] :
12
- https://sveltesociety.dev/recipes/ testing- and-debugging/unit-testing-svelte-component
9
+ See the [ svelte- testing-library repository ] [ repository-examples ] for detailed
10
+ examples, including examples for testing older Svelte 3 and 4 components.
13
11
14
- ## Basic
12
+ :::
15
13
16
14
This basic example demonstrates how to:
17
15
18
- - Pass props to your Svelte component using ` render `
19
- - Query the structure of your component's DOM elements using ` screen `
16
+ - Pass props to your Svelte component using [ ` render() ` ] [ render ]
17
+ - [ Query] [ ] the structure of your component's DOM elements using
18
+ [ ` screen ` ] [ screen ]
20
19
- Interact with your component using [ ` @testing-library/user-event ` ] [ user-event ]
21
20
- Make assertions using ` expect ` , using matchers from
22
21
[ ` @testing-library/jest-dom ` ] [ jest-dom ]
23
22
24
- ``` html title="greeter.svelte"
23
+ For additional resources, patterns, and best practices about testing Svelte
24
+ components and other Svelte features, take a look at the [ Svelte Society testing
25
+ recipes] [ testing-recipes ] .
26
+
27
+ ``` html title="basic.svelte"
25
28
<script >
26
- export let name
29
+ let { name} = $props ()
27
30
28
- let showGreeting = false
31
+ let showGreeting = $state ( false )
29
32
30
- const handleClick = () => (showGreeting = true )
33
+ const onclick = () => (showGreeting = true )
31
34
</script >
32
35
33
- <button on:click = " {handleClick} " >Greet</button >
36
+ <button {onclick} >Greet</button >
34
37
35
38
{#if showGreeting}
36
39
<p >Hello {name}</p >
37
40
{/if}
38
41
```
39
42
40
- ``` js title="greeter .test.js"
43
+ ``` js title="basic .test.js"
41
44
import {render , screen } from ' @testing-library/svelte'
42
- import userEvent from ' @testing-library/user-event'
45
+ import { userEvent } from ' @testing-library/user-event'
43
46
import {expect , test } from ' vitest'
44
47
45
- import Greeter from ' ./greeter .svelte'
48
+ import Subject from ' ./basic .svelte'
46
49
47
50
test (' no initial greeting' , () => {
48
- render (Greeter , {name: ' World' })
51
+ render (Subject , {name: ' World' })
49
52
50
53
const button = screen .getByRole (' button' , {name: ' Greet' })
51
54
const greeting = screen .queryByText (/ hello/ iu )
@@ -56,7 +59,7 @@ test('no initial greeting', () => {
56
59
57
60
test (' greeting appears on click' , async () => {
58
61
const user = userEvent .setup ()
59
- render (Greeter , {name: ' World' })
62
+ render (Subject , {name: ' World' })
60
63
61
64
const button = screen .getByRole (' button' )
62
65
await user .click (button)
@@ -66,213 +69,12 @@ test('greeting appears on click', async () => {
66
69
})
67
70
```
68
71
72
+ [ repository-examples] :
73
+ https://github.com/testing-library/svelte-testing-library/tree/main/examples
74
+ [ testing-recipes] :
75
+ https://sveltesociety.dev/recipes/testing-and-debugging/unit-testing-svelte-component
76
+ [ query ] : ../queries/about.mdx
77
+ [ screen ] : ../queries/about.mdx#screen
78
+ [ render ] : ./api.mdx#render
69
79
[ user-event ] : ../user-event/intro.mdx
70
80
[ jest-dom ] : https://github.com/testing-library/jest-dom
71
-
72
- ## Events
73
-
74
- Events can be tested using spy functions. If you're using Vitest you can use
75
- [ ` vi.fn() ` ] [ vi-fn ] to create a spy.
76
-
77
- :::info
78
-
79
- Consider using function props to make testing events easier.
80
-
81
- :::
82
-
83
- ``` html title="button-with-event.svelte"
84
- <button on:click >click me</button >
85
- ```
86
-
87
- ``` html title="button-with-prop.svelte"
88
- <script >
89
- export let onClick
90
- </script >
91
-
92
- <button on:click =" {onClick}" >click me</button >
93
- ```
94
-
95
- ``` js title="button.test.ts"
96
- import {render , screen } from ' @testing-library/svelte'
97
- import userEvent from ' @testing-library/user-event'
98
- import {expect , test , vi } from ' vitest'
99
-
100
- import ButtonWithEvent from ' ./button-with-event.svelte'
101
- import ButtonWithProp from ' ./button-with-prop.svelte'
102
-
103
- test (' button with event' , async () => {
104
- const user = userEvent .setup ()
105
- const onClick = vi .fn ()
106
-
107
- const {component } = render (ButtonWithEvent)
108
- component .$on (' click' , onClick)
109
-
110
- const button = screen .getByRole (' button' )
111
- await user .click (button)
112
-
113
- expect (onClick).toHaveBeenCalledOnce ()
114
- })
115
-
116
- test (' button with function prop' , async () => {
117
- const user = userEvent .setup ()
118
- const onClick = vi .fn ()
119
-
120
- render (ButtonWithProp, {onClick})
121
-
122
- const button = screen .getByRole (' button' )
123
- await user .click (button)
124
-
125
- expect (onClick).toHaveBeenCalledOnce ()
126
- })
127
- ```
128
-
129
- [ vi-fn ] : https://vitest.dev/api/vi.html#vi-fn
130
-
131
- ## Slots
132
-
133
- Slots cannot be tested directly. It's usually easier to structure your code so
134
- that you can test the user-facing results, leaving any slots as an
135
- implementation detail.
136
-
137
- However, if slots are an important developer-facing API of your component, you
138
- can use a wrapper component and "dummy" children to test them. Test IDs can be
139
- helpful when testing slots in this manner.
140
-
141
- ``` html title="heading.svelte"
142
- <h1 >
143
- <slot />
144
- </h1 >
145
- ```
146
-
147
- ``` html title="heading.test.svelte"
148
- <script >
149
- import Heading from ' ./heading.svelte'
150
- </script >
151
-
152
- <Heading >
153
- <span data-testid =" child" />
154
- </Heading >
155
- ```
156
-
157
- ``` js title="heading.test.js"
158
- import {render , screen , within } from ' @testing-library/svelte'
159
- import {expect , test } from ' vitest'
160
-
161
- import HeadingTest from ' ./heading.test.svelte'
162
-
163
- test (' heading with slot' , () => {
164
- render (HeadingTest)
165
-
166
- const heading = screen .getByRole (' heading' )
167
- const child = within (heading).getByTestId (' child' )
168
-
169
- expect (child).toBeInTheDocument ()
170
- })
171
- ```
172
-
173
- ## Two-way data binding
174
-
175
- Two-way data binding cannot be tested directly. It's usually easier to structure
176
- your code so that you can test the user-facing results, leaving the binding as
177
- an implementation detail.
178
-
179
- However, if two-way binding is an important developer-facing API of your
180
- component, you can use a wrapper component and writable store to test the
181
- binding itself.
182
-
183
- ``` html title="text-input.svelte"
184
- <script >
185
- export let value = ' '
186
- </script >
187
-
188
- <input type =" text" bind:value =" {value}" />
189
- ```
190
-
191
- ``` html title="text-input.test.svelte"
192
- <script >
193
- import TextInput from ' ./text-input.svelte'
194
-
195
- export let valueStore
196
- </script >
197
-
198
- <TextInput bind:value =" {$valueStore}" />
199
- ```
200
-
201
- ``` js title="text-input.test.js"
202
- import {render , screen } from ' @testing-library/svelte'
203
- import userEvent from ' @testing-library/user-event'
204
- import {get , writable } from ' svelte/store'
205
- import {expect , test } from ' vitest'
206
-
207
- import TextInputTest from ' ./text-input.test.svelte'
208
-
209
- test (' text input with value binding' , async () => {
210
- const user = userEvent .setup ()
211
- const valueStore = writable (' ' )
212
-
213
- render (TextInputTest, {valueStore})
214
-
215
- const input = screen .getByRole (' textbox' )
216
- await user .type (input, ' hello world' )
217
-
218
- expect (get (valueStore)).toBe (' hello world' )
219
- })
220
- ```
221
-
222
- ## Contexts
223
-
224
- If your component requires access to contexts, you can pass those contexts in
225
- when you [ ` render ` ] [ component-options ] the component. When you use options like
226
- ` context ` , be sure to place props under the ` props ` key.
227
-
228
- [ component-options ] : ./api.mdx#component-options
229
-
230
- ``` html title="notifications-provider.svelte"
231
- <script >
232
- import {setContext } from ' svelte'
233
- import {writable } from ' svelte/stores'
234
-
235
- setContext (' messages' , writable ([]))
236
- </script >
237
- ```
238
-
239
- ``` html title="notifications.svelte"
240
- <script >
241
- import {getContext } from ' svelte'
242
-
243
- export let label
244
-
245
- const messages = getContext (' messages' )
246
- </script >
247
-
248
- <div role =" status" aria-label =" {label}" >
249
- {#each $messages as message (message.id)}
250
- <p >{message.text}</p >
251
- <hr />
252
- {/each}
253
- </div >
254
- ```
255
-
256
- ``` js title="notifications.test.js"
257
- import {render , screen } from ' @testing-library/svelte'
258
- import {readable } from ' svelte/store'
259
- import {expect , test } from ' vitest'
260
-
261
- import Notifications from ' ./notifications.svelte'
262
-
263
- test (' notifications with messages from context' , async () => {
264
- const messages = readable ([
265
- {id: ' abc' , text: ' hello' },
266
- {id: ' def' , text: ' world' },
267
- ])
268
-
269
- render (Notifications, {
270
- context: new Map ([[' messages' , messages]]),
271
- props: {label: ' Notifications' },
272
- })
273
-
274
- const status = screen .getByRole (' status' , {name: ' Notifications' })
275
-
276
- expect (status).toHaveTextContent (' hello world' )
277
- })
278
- ```
0 commit comments