Skip to content

Commit 2d425a4

Browse files
committed
docs(examples): add examples to repository
1 parent 492dbd1 commit 2d425a4

33 files changed

+741
-17
lines changed

.all-contributorsrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
"name": "Michael Cousins",
110110
"avatar_url": "https://avatars.githubusercontent.com/u/2963448?v=4",
111111
"profile": "https://michael.cousins.io/",
112-
"contributions": ["code"]
112+
"contributions": ["code", "doc", "ideas", "maintenance", "test"]
113113
}
114114
],
115115
"contributorsPerLine": 7,

.github/workflows/release.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ jobs:
3737
- { svelte: '5', node: '16' }
3838
- { svelte: '5', node: '18' }
3939
include:
40-
# We only need to lint once, so do it on latest Node and Svelte
40+
# Only lint and test examples on latest Node and Svelte
4141
- { svelte: '5', node: '22', check: 'lint' }
42+
- { svelte: '5', node: '22', check: 'test:examples' }
4243
# Run type checks in latest applicable Node
4344
- { svelte: '3', node: '20', check: 'types:legacy' }
4445
- { svelte: '4', node: '22', check: 'types:legacy' }

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ npm run all:legacy
7777

7878
### Docs
7979

80-
Use the `toc` script to ensure the README's table of contents is up to date:
80+
Use the `docs` script to ensure the README's table of contents is up to date:
8181

8282
```shell
83-
npm run toc
83+
npm run docs
8484
```
8585

8686
Use `contributors:add` to add a contributor to the README:

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212

1313
<p>Simple and complete Svelte testing utilities that encourage good testing practices.</p>
1414

15-
[**Read The Docs**][stl-docs] | [Edit the docs][stl-docs-repo]
15+
[**Read The Docs**][stl-docs] | [Edit the docs][stl-docs-repo] | [Examples](./examples)
1616

1717
<!-- prettier-ignore-start -->
18+
1819
[![Build Status][build-badge]][build]
1920
[![Code Coverage][coverage-badge]][coverage]
2021
[![version][version-badge]][package]
@@ -29,7 +30,9 @@
2930
[![Watch on GitHub][github-watch-badge]][github-watch]
3031
[![Star on GitHub][github-star-badge]][github-star]
3132
[![Tweet][twitter-badge]][twitter]
33+
3234
<!-- prettier-ignore-end -->
35+
3336
</div>
3437

3538
<hr />
@@ -63,9 +66,6 @@
6366

6467
## Table of Contents
6568

66-
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
67-
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
68-
6969
- [The Problem](#the-problem)
7070
- [This Solution](#this-solution)
7171
- [Installation](#installation)
@@ -78,8 +78,6 @@
7878
- [❓ Questions](#-questions)
7979
- [Contributors](#contributors)
8080

81-
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
82-
8381
## The Problem
8482

8583
You want to write maintainable tests for your [Svelte][svelte] components.
@@ -217,8 +215,11 @@ instead of filing an issue on GitHub.
217215
Thanks goes to these people ([emoji key][emojis]):
218216

219217
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
218+
220219
<!-- prettier-ignore-start -->
220+
221221
<!-- markdownlint-disable -->
222+
222223
<table>
223224
<tbody>
224225
<tr>
@@ -240,12 +241,13 @@ Thanks goes to these people ([emoji key][emojis]):
240241
<td align="center" valign="top" width="14.28%"><a href="https://techblog.babyl.ca/"><img src="https://avatars.githubusercontent.com/u/19954?v=4?s=100" width="100px;" alt="Yanick Champoux"/><br /><sub><b>Yanick Champoux</b></sub></a><br /><a href="https://github.com/testing-library/svelte-testing-library/commits?author=yanick" title="Code">💻</a></td>
241242
</tr>
242243
<tr>
243-
<td align="center" valign="top" width="14.28%"><a href="https://michael.cousins.io/"><img src="https://avatars.githubusercontent.com/u/2963448?v=4?s=100" width="100px;" alt="Michael Cousins"/><br /><sub><b>Michael Cousins</b></sub></a><br /><a href="https://github.com/testing-library/svelte-testing-library/commits?author=mcous" title="Code">💻</a></td>
244+
<td align="center" valign="top" width="14.28%"><a href="https://michael.cousins.io/"><img src="https://avatars.githubusercontent.com/u/2963448?v=4?s=100" width="100px;" alt="Michael Cousins"/><br /><sub><b>Michael Cousins</b></sub></a><br /><a href="https://github.com/testing-library/svelte-testing-library/commits?author=mcous" title="Code">💻</a> <a href="https://github.com/testing-library/svelte-testing-library/commits?author=mcous" title="Documentation">📖</a> <a href="#ideas-mcous" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-mcous" title="Maintenance">🚧</a> <a href="https://github.com/testing-library/svelte-testing-library/commits?author=mcous" title="Tests">⚠️</a></td>
244245
</tr>
245246
</tbody>
246247
</table>
247248

248249
<!-- markdownlint-restore -->
250+
249251
<!-- prettier-ignore-end -->
250252

251253
<!-- ALL-CONTRIBUTORS-LIST:END -->

eslint.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ export default tseslint.config(
7575
files: ['**/*.svelte'],
7676
rules: {
7777
'svelte/no-unused-svelte-ignore': 'off',
78-
'unicorn/filename-case': ['error', { case: 'pascalCase' }],
78+
'unicorn/filename-case': [
79+
'error',
80+
{ cases: { kebabCase: true, pascalCase: true } },
81+
],
7982
'unicorn/no-useless-undefined': 'off',
8083
},
8184
},

examples/basic/basic.svelte

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
let { name } = $props()
3+
4+
let showGreeting = $state(false)
5+
6+
const onclick = () => (showGreeting = true)
7+
</script>
8+
9+
<button {onclick}>Greet</button>
10+
11+
{#if showGreeting}
12+
<p>Hello {name}</p>
13+
{/if}

examples/basic/basic.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { render, screen } from '@testing-library/svelte'
2+
import { userEvent } from '@testing-library/user-event'
3+
import { expect, test } from 'vitest'
4+
5+
import Subject from './basic.svelte'
6+
7+
test('no initial greeting', () => {
8+
render(Subject, { name: 'World' })
9+
10+
const button = screen.getByRole('button', { name: 'Greet' })
11+
const greeting = screen.queryByText(/hello/iu)
12+
13+
expect(button).toBeInTheDocument()
14+
expect(greeting).not.toBeInTheDocument()
15+
})
16+
17+
test('greeting appears on click', async () => {
18+
const user = userEvent.setup()
19+
render(Subject, { name: 'World' })
20+
21+
const button = screen.getByRole('button')
22+
await user.click(button)
23+
const greeting = screen.getByText(/hello world/iu)
24+
25+
expect(greeting).toBeInTheDocument()
26+
})

examples/basic/readme.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Basic
2+
3+
This basic example demonstrates how to:
4+
5+
- Pass props to your Svelte component using [render()]
6+
- [Query][] the structure of your component's DOM elements using screen
7+
- Interact with your component using [@testing-library/user-event][]
8+
- Make assertions using expect, using matchers from
9+
[@testing-library/jest-dom][]
10+
11+
[query]: https://testing-library.com/docs/queries/about
12+
[render()]: https://testing-library.com/docs/svelte-testing-library/api#render
13+
[@testing-library/user-event]: https://testing-library.com/docs/user-event/intro
14+
[@testing-library/jest-dom]: https://github.com/testing-library/jest-dom
15+
16+
## Table of contents
17+
18+
- [`basic.svelte`](#basicsvelte)
19+
- [`basic.test.js`](#basictestjs)
20+
21+
## `basic.svelte`
22+
23+
```svelte file=./basic.svelte
24+
<script>
25+
let { name } = $props()
26+
27+
let showGreeting = $state(false)
28+
29+
const onclick = () => (showGreeting = true)
30+
</script>
31+
32+
<button {onclick}>Greet</button>
33+
34+
{#if showGreeting}
35+
<p>Hello {name}</p>
36+
{/if}
37+
```
38+
39+
## `basic.test.js`
40+
41+
```js file=./basic.test.js
42+
import { render, screen } from '@testing-library/svelte'
43+
import { userEvent } from '@testing-library/user-event'
44+
import { expect, test } from 'vitest'
45+
46+
import Subject from './basic.svelte'
47+
48+
test('no initial greeting', () => {
49+
render(Subject, { name: 'World' })
50+
51+
const button = screen.getByRole('button', { name: 'Greet' })
52+
const greeting = screen.queryByText(/hello/iu)
53+
54+
expect(button).toBeInTheDocument()
55+
expect(greeting).not.toBeInTheDocument()
56+
})
57+
58+
test('greeting appears on click', async () => {
59+
const user = userEvent.setup()
60+
render(Subject, { name: 'World' })
61+
62+
const button = screen.getByRole('button')
63+
await user.click(button)
64+
const greeting = screen.getByText(/hello world/iu)
65+
66+
expect(greeting).toBeInTheDocument()
67+
})
68+
```

examples/binds/bind.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let { value = $bindable('') } = $props()
3+
</script>
4+
5+
<input type="text" bind:value />

examples/binds/bind.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { render, screen } from '@testing-library/svelte'
2+
import { userEvent } from '@testing-library/user-event'
3+
import { expect, test } from 'vitest'
4+
5+
import Subject from './bind.svelte'
6+
7+
test('value binding', async () => {
8+
const user = userEvent.setup()
9+
let value = ''
10+
11+
render(Subject, {
12+
get value() {
13+
return value
14+
},
15+
set value(nextValue) {
16+
value = nextValue
17+
},
18+
})
19+
20+
const input = screen.getByRole('textbox')
21+
await user.type(input, 'hello world')
22+
23+
expect(value).toBe('hello world')
24+
})

examples/binds/no-bind.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
let { value, onInput } = $props()
3+
4+
const oninput = (event) => {
5+
onInput(event.target.value)
6+
}
7+
</script>
8+
9+
<input type="text" {value} {oninput} />

examples/binds/readme.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Binds
2+
3+
Two-way data binding using [bindable() props][] is difficult to test directly.
4+
It's usually easier to structure your code so that you can test user-facing
5+
results, leaving the binding as an implementation detail.
6+
7+
However, if two-way binding is an important developer-facing API of your
8+
component, you can use setters to test your binding.
9+
10+
[bindable() props]: https://svelte.dev/docs/svelte/$bindable
11+
12+
## Table of contents
13+
14+
- [`bind.svelte`](#bindsvelte)
15+
- [`bind.test.js`](#bindtestjs)
16+
- [Consider avoiding binding](#consider-avoiding-binding)
17+
18+
## `bind.svelte`
19+
20+
```svelte file=./bind.svelte
21+
<script>
22+
let { value = $bindable('') } = $props()
23+
</script>
24+
25+
<input type="text" bind:value />
26+
```
27+
28+
## `bind.test.js`
29+
30+
```svelte file=./bind.test.js
31+
import { render, screen } from '@testing-library/svelte'
32+
import { userEvent } from '@testing-library/user-event'
33+
import { expect, test } from 'vitest'
34+
35+
import Subject from './bind.svelte'
36+
37+
test('value binding', async () => {
38+
const user = userEvent.setup()
39+
let value = ''
40+
41+
render(Subject, {
42+
get value() {
43+
return value
44+
},
45+
set value(nextValue) {
46+
value = nextValue
47+
},
48+
})
49+
50+
const input = screen.getByRole('textbox')
51+
await user.type(input, 'hello world')
52+
53+
expect(value).toBe('hello world')
54+
})
55+
```
56+
57+
## Consider avoiding binding
58+
59+
Before embarking on writing tests for bindable props, consider avoiding
60+
`bindable()` entirely. Two-way data binding can make your data flows and state
61+
changes difficult to reason about and test effectively. Instead, you can use
62+
value props to pass data down and callback props to pass changes back up to the
63+
parent.
64+
65+
> Well-written applications use bindings very sparingly — the vast majority of
66+
> data flow should be top-down --
67+
> <cite>[Rich Harris](https://github.com/sveltejs/svelte/issues/10768#issue-2181814844)</cite>
68+
69+
For example, rather than using a `bindable()` prop, use a value prop to pass the
70+
value down and callback prop to send changes back up to the parent:
71+
72+
```svelte file=./no-bind.svelte
73+
<script>
74+
let { value, onInput } = $props()
75+
76+
const oninput = (event) => {
77+
onInput(event.target.value)
78+
}
79+
</script>
80+
81+
<input type="text" {value} {oninput} />
82+
```

examples/contexts/context.svelte

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<script>
2+
import { getContext } from 'svelte'
3+
4+
let { label } = $props()
5+
6+
const messages = getContext('messages')
7+
</script>
8+
9+
<div role="status" aria-label={label}>
10+
{#each messages.current as message (message.id)}
11+
<p>{message.text}</p>
12+
<hr />
13+
{/each}
14+
</div>

examples/contexts/context.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { render, screen } from '@testing-library/svelte'
2+
import { expect, test } from 'vitest'
3+
4+
import Subject from './context.svelte'
5+
6+
test('notifications with messages from context', async () => {
7+
const messages = {
8+
get current() {
9+
return [
10+
{ id: 'abc', text: 'hello' },
11+
{ id: 'def', text: 'world' },
12+
]
13+
},
14+
}
15+
16+
render(Subject, {
17+
context: new Map([['messages', messages]]),
18+
props: { label: 'Notifications' },
19+
})
20+
21+
const status = screen.getByRole('status', { name: 'Notifications' })
22+
23+
expect(status).toHaveTextContent('hello world')
24+
})

0 commit comments

Comments
 (0)