Skip to content

Commit 380f457

Browse files
Merge branch 'elliott/effect-pending-correct-boundary' into adjust-boundary-error-message
2 parents 3c73cc7 + bd6a414 commit 380f457

File tree

70 files changed

+1016
-120
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1016
-120
lines changed

.changeset/dirty-cycles-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: send `$effect.pending` count to the correct boundary

.changeset/silent-suns-whisper.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

documentation/docs/07-misc/02-testing.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ export function logger(getValue) {
160160

161161
### Component testing
162162

163-
It is possible to test your components in isolation using Vitest.
163+
It is possible to test your components in isolation, which allows you to render them in a browser (real or simulated), simulate behavior, and make assertions, without spinning up your whole app.
164164

165-
> [!NOTE] Before writing component tests, think about whether you actually need to test the component, or if it's more about the logic _inside_ the component. If so, consider extracting out that logic to test it in isolation, without the overhead of a component
165+
> [!NOTE] Before writing component tests, think about whether you actually need to test the component, or if it's more about the logic _inside_ the component. If so, consider extracting out that logic to test it in isolation, without the overhead of a component.
166166
167167
To get started, install jsdom (a library that shims DOM APIs):
168168

@@ -246,6 +246,48 @@ test('Component', async () => {
246246

247247
When writing component tests that involve two-way bindings, context or snippet props, it's best to create a wrapper component for your specific test and interact with that. `@testing-library/svelte` contains some [examples](https://testing-library.com/docs/svelte-testing-library/example).
248248

249+
### Component testing with Storybook
250+
251+
[Storybook](https://storybook.js.org) is a tool for developing and documenting UI components, and it can also be used to test your components. They're run with Vitest's browser mode, which renders your components in a real browser for the most realistic testing environment.
252+
253+
To get started, first install Storybook ([using Svelte's CLI](/docs/cli/storybook)) in your project via `npx sv add storybook` and choose the recommended configuration that includes testing features. If you're already using Storybook, and for more information on Storybook's testing capabilities, follow the [Storybook testing docs](https://storybook.js.org/docs/writing-tests?renderer=svelte) to get started.
254+
255+
You can create stories for component variations and test interactions with the [play function](https://storybook.js.org/docs/writing-tests/interaction-testing?renderer=svelte#writing-interaction-tests), which allows you to simulate behavior and make assertions using the Testing Library and Vitest APIs. Here's an example of two stories that can be tested, one that renders an empty LoginForm component and one that simulates a user filling out the form:
256+
257+
```svelte
258+
/// file: LoginForm.stories.svelte
259+
<script module>
260+
import { defineMeta } from '@storybook/addon-svelte-csf';
261+
import { expect, fn } from 'storybook/test';
262+
263+
import LoginForm from './LoginForm.svelte';
264+
265+
const { Story } = defineMeta({
266+
component: LoginForm,
267+
args: {
268+
// Pass a mock function to the `onSubmit` prop
269+
onSubmit: fn(),
270+
}
271+
});
272+
</script>
273+
274+
<Story name="Empty Form" />
275+
276+
<Story
277+
name="Filled Form"
278+
play={async ({ args, canvas, userEvent }) => {
279+
// Simulate a user filling out the form
280+
await userEvent.type(canvas.getByTestId('email'), '[email protected]');
281+
await userEvent.type(canvas.getByTestId('password'), 'a-random-password');
282+
await userEvent.click(canvas.getByRole('button'));
283+
284+
// Run assertions
285+
await expect(args.onSubmit).toHaveBeenCalledTimes(1);
286+
await expect(canvas.getByText('You’re in!')).toBeInTheDocument();
287+
}}
288+
/>
289+
```
290+
249291
## E2E tests using Playwright
250292

251293
E2E (short for 'end to end') tests allow you to test your full application through the eyes of the user. This section uses [Playwright](https://playwright.dev/) as an example, but you can also use other solutions like [Cypress](https://www.cypress.io/) or [NightwatchJS](https://nightwatchjs.org/).

documentation/docs/07-misc/04-custom-elements.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Custom elements
44

55
<!-- - [basically what we have today](https://svelte.dev/docs/custom-elements-api) -->
66

7-
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](svelte-options).
7+
Svelte components can also be compiled to custom elements (aka web components) using the `customElement: true` compiler option. You should specify a tag name for the component using the `<svelte:options>` [element](svelte-options). Within the custom element you can access the host element via the [`$host`](https://svelte.dev/docs/svelte/$host) rune.
88

99
```svelte
1010
<svelte:options customElement="my-element" />

documentation/docs/98-reference/.generated/compile-errors.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,51 @@ Cyclical dependency detected: %cycle%
196196
`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`
197197
```
198198

199+
### const_tag_invalid_reference
200+
201+
```
202+
The `{@const %name% = ...}` declaration is not available in this snippet
203+
```
204+
205+
The following is an error:
206+
207+
```svelte
208+
<svelte:boundary>
209+
{@const foo = 'bar'}
210+
211+
{#snippet failed()}
212+
{foo}
213+
{/snippet}
214+
</svelte:boundary>
215+
```
216+
217+
Here, `foo` is not available inside `failed`. The top level code inside `<svelte:boundary>` becomes part of the implicit `children` snippet, in other words the above code is equivalent to this:
218+
219+
```svelte
220+
<svelte:boundary>
221+
{#snippet children()}
222+
{@const foo = 'bar'}
223+
{/snippet}
224+
225+
{#snippet failed()}
226+
{foo}
227+
{/snippet}
228+
</svelte:boundary>
229+
```
230+
231+
The same applies to components:
232+
233+
```svelte
234+
<Component>
235+
{@const foo = 'bar'}
236+
237+
{#snippet someProp()}
238+
<!-- error -->
239+
{foo}
240+
{/snippet}
241+
</Component>
242+
```
243+
199244
### constant_assignment
200245

201246
```

packages/svelte/CHANGELOG.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,55 @@
11
# svelte
22

3+
## 5.38.7
4+
5+
### Patch Changes
6+
7+
- fix: replace `undefined` with `void(0)` in CallExpressions ([#16693](https://github.com/sveltejs/svelte/pull/16693))
8+
9+
- fix: ensure batch exists when resetting a failed boundary ([#16698](https://github.com/sveltejs/svelte/pull/16698))
10+
11+
- fix: place store setup inside async body ([#16687](https://github.com/sveltejs/svelte/pull/16687))
12+
13+
## 5.38.6
14+
15+
### Patch Changes
16+
17+
- fix: don't fail on `flushSync` while flushing effects ([#16674](https://github.com/sveltejs/svelte/pull/16674))
18+
19+
## 5.38.5
20+
21+
### Patch Changes
22+
23+
- fix: ensure async deriveds always get dependencies from thennable ([#16672](https://github.com/sveltejs/svelte/pull/16672))
24+
25+
## 5.38.4
26+
27+
### Patch Changes
28+
29+
- fix: place instance-level snippets inside async body ([#16666](https://github.com/sveltejs/svelte/pull/16666))
30+
31+
- fix: Add check for builtin custom elements in `set_custom_element_data` ([#16592](https://github.com/sveltejs/svelte/pull/16592))
32+
33+
- fix: restore batch along with effect context ([#16668](https://github.com/sveltejs/svelte/pull/16668))
34+
35+
- fix: wait until changes propagate before updating input selection state ([#16649](https://github.com/sveltejs/svelte/pull/16649))
36+
37+
- fix: add "Accept-CH" as valid value for `http-equiv` ([#16671](https://github.com/sveltejs/svelte/pull/16671))
38+
39+
## 5.38.3
40+
41+
### Patch Changes
42+
43+
- fix: ensure correct order of template effect values ([#16655](https://github.com/sveltejs/svelte/pull/16655))
44+
45+
- fix: allow async `{@const}` in more places ([#16643](https://github.com/sveltejs/svelte/pull/16643))
46+
47+
- fix: properly catch top level await errors ([#16619](https://github.com/sveltejs/svelte/pull/16619))
48+
49+
- perf: prune effects without dependencies ([#16625](https://github.com/sveltejs/svelte/pull/16625))
50+
51+
- fix: only emit `for_await_track_reactivity_loss` in async mode ([#16644](https://github.com/sveltejs/svelte/pull/16644))
52+
353
## 5.38.2
454

555
### Patch Changes

packages/svelte/elements.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,7 @@ export interface HTMLMetaAttributes extends HTMLAttributes<HTMLMetaElement> {
12681268
charset?: string | undefined | null;
12691269
content?: string | undefined | null;
12701270
'http-equiv'?:
1271+
| 'accept-ch'
12711272
| 'content-security-policy'
12721273
| 'content-type'
12731274
| 'default-style'

packages/svelte/messages/compile-errors/template.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,49 @@
124124

125125
> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`
126126
127+
## const_tag_invalid_reference
128+
129+
> The `{@const %name% = ...}` declaration is not available in this snippet
130+
131+
The following is an error:
132+
133+
```svelte
134+
<svelte:boundary>
135+
{@const foo = 'bar'}
136+
137+
{#snippet failed()}
138+
{foo}
139+
{/snippet}
140+
</svelte:boundary>
141+
```
142+
143+
Here, `foo` is not available inside `failed`. The top level code inside `<svelte:boundary>` becomes part of the implicit `children` snippet, in other words the above code is equivalent to this:
144+
145+
```svelte
146+
<svelte:boundary>
147+
{#snippet children()}
148+
{@const foo = 'bar'}
149+
{/snippet}
150+
151+
{#snippet failed()}
152+
{foo}
153+
{/snippet}
154+
</svelte:boundary>
155+
```
156+
157+
The same applies to components:
158+
159+
```svelte
160+
<Component>
161+
{@const foo = 'bar'}
162+
163+
{#snippet someProp()}
164+
<!-- error -->
165+
{foo}
166+
{/snippet}
167+
</Component>
168+
```
169+
127170
## debug_tag_invalid_arguments
128171

129172
> {@debug ...} arguments must be identifiers, not arbitrary expressions

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.38.2",
5+
"version": "5.38.7",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/compiler/errors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,16 @@ export function const_tag_invalid_placement(node) {
985985
e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\`, \`<svelte:boundary\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`);
986986
}
987987

988+
/**
989+
* The `{@const %name% = ...}` declaration is not available in this snippet
990+
* @param {null | number | NodeLike} node
991+
* @param {string} name
992+
* @returns {never}
993+
*/
994+
export function const_tag_invalid_reference(node, name) {
995+
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet \nhttps://svelte.dev/e/const_tag_invalid_reference`);
996+
}
997+
988998
/**
989999
* {@debug ...} arguments must be identifiers, not arbitrary expressions
9901000
* @param {null | number | NodeLike} node

0 commit comments

Comments
 (0)