Skip to content

Commit c4a1543

Browse files
committed
fix(svelte5): use state rune for rerender
1 parent 266e2df commit c4a1543

9 files changed

+111
-49
lines changed

.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@ module.exports = {
4949
ecmaVersion: 2022,
5050
sourceType: 'module',
5151
},
52+
globals: { $state: 'readonly' },
5253
ignorePatterns: ['!/.*'],
5354
}

src/__tests__/fixtures/Comp.svelte

-2
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,3 @@
1313
<h1 data-testid="test">Hello {name}!</h1>
1414

1515
<button on:click={handleClick}>{buttonText}</button>
16-
17-
<style></style>
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
let { name = 'World' } = $props()
3+
4+
let buttonText = $state('Button')
5+
6+
function handleClick() {
7+
buttonText = 'Button Clicked'
8+
}
9+
</script>
10+
11+
<h1 data-testid="test">Hello {name}!</h1>
12+
13+
<button onclick={handleClick}>{buttonText}</button>

src/__tests__/render.test.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import { render } from '@testing-library/svelte'
2-
import { describe, expect, test } from 'vitest'
2+
import { beforeAll, describe, expect, test } from 'vitest'
33

4-
import Comp from './fixtures/Comp.svelte'
54
import { IS_SVELTE_5 } from './utils.js'
65

7-
describe('render', () => {
6+
describe.each([
7+
{ name: 'legacy', componentImport: './fixtures/Comp.svelte' },
8+
...(IS_SVELTE_5
9+
? [{ name: 'runes', componentImport: './fixtures/CompRunes.svelte' }]
10+
: []),
11+
])('render $name component', ({ componentImport }) => {
812
const props = { name: 'World' }
13+
let Comp
14+
15+
beforeAll(async () => {
16+
Comp = await import(componentImport)
17+
})
918

1019
test('renders component into the document', () => {
1120
const { getByText } = render(Comp, { props })

src/__tests__/rerender.test.js

+28-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
import { act, render, screen } from '@testing-library/svelte'
2-
import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
3-
import { describe, expect, test, vi } from 'vitest'
2+
import { beforeAll, describe, expect, test, vi } from 'vitest'
43

5-
import Comp from './fixtures/Comp.svelte'
4+
import { IS_SVELTE_5 } from './utils.js'
5+
6+
describe.each([
7+
{
8+
name: 'legacy',
9+
componentImport: './fixtures/Comp.svelte',
10+
},
11+
...(IS_SVELTE_5
12+
? [
13+
{
14+
name: 'runes',
15+
componentImport: './fixtures/CompRunes.svelte',
16+
},
17+
]
18+
: []),
19+
])('rerender $name component', ({ name, componentImport, accessors }) => {
20+
let Comp
21+
22+
beforeAll(async () => {
23+
Comp = await import(componentImport)
24+
})
625

7-
describe('rerender', () => {
826
test('updates props', async () => {
927
const { rerender } = render(Comp, { name: 'World' })
1028
const element = screen.getByText('Hello World!')
@@ -29,13 +47,12 @@ describe('rerender', () => {
2947
)
3048
})
3149

32-
test('change props with accessors', async () => {
33-
const { component, getByText } = render(
34-
Comp,
35-
SVELTE_VERSION < '5'
36-
? { accessors: true, props: { name: 'World' } }
37-
: { name: 'World' }
38-
)
50+
test.skipIf(name === 'runes')('change props with accessors', async () => {
51+
const componentOptions = IS_SVELTE_5
52+
? { name: 'World' }
53+
: { accessors: true, props: { name: 'World' } }
54+
55+
const { component, getByText } = render(Comp, componentOptions)
3956
const element = getByText('Hello World!')
4057

4158
expect(element).toBeInTheDocument()

src/pure.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ export class SvelteTestingLibrary {
8080
)
8181
props = props.props
8282
}
83-
component.$set(props)
83+
84+
this.rerenderComponent(component, props)
8485
await Svelte.tick()
8586
},
8687
unmount: () => {
@@ -107,6 +108,10 @@ export class SvelteTestingLibrary {
107108
return component
108109
}
109110

111+
rerenderComponent(component, nextProps) {
112+
component.$set(nextProps)
113+
}
114+
110115
cleanupComponent(component) {
111116
const inCache = this.componentCache.delete(component)
112117

src/svelte5-index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable import/export */
22
import { act } from './pure.js'
3-
import { cleanup } from './svelte5.js'
3+
import { cleanup } from './svelte5.svelte.js'
44

55
// If we're running in a test runner that supports afterEach
66
// then we'll automatically run cleanup afterEach test
@@ -20,4 +20,4 @@ export * from '@testing-library/dom'
2020
// export svelte-specific functions and custom `fireEvent`
2121
// `fireEvent` must be a named export to take priority over wildcard export above
2222
export { act, fireEvent } from './pure.js'
23-
export { cleanup, render } from './svelte5.js'
23+
export { cleanup, render } from './svelte5.svelte.js'

src/svelte5.js

-30
This file was deleted.

src/svelte5.svelte.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/** eslint-global: $state */
2+
import { mount, unmount } from 'svelte'
3+
4+
import { SvelteTestingLibrary } from './pure.js'
5+
6+
class Svelte5TestingLibrary extends SvelteTestingLibrary {
7+
svelteComponentOptions = [
8+
'target',
9+
'props',
10+
'events',
11+
'context',
12+
'intro',
13+
'recover',
14+
]
15+
16+
propsByComponent = new Map()
17+
18+
renderComponent(ComponentConstructor, componentOptions) {
19+
const props = $state(componentOptions.props ?? {})
20+
const component = mount(ComponentConstructor, {
21+
...componentOptions,
22+
props,
23+
})
24+
25+
this.componentCache.add(component)
26+
this.propsByComponent.set(component, props)
27+
28+
return component
29+
}
30+
31+
rerenderComponent(component, nextProps) {
32+
const prevProps = this.propsByComponent.get(component)
33+
Object.assign(prevProps, nextProps)
34+
}
35+
36+
cleanupComponent(component) {
37+
const inCache = this.componentCache.delete(component)
38+
this.propsByComponent.delete(component)
39+
40+
if (inCache) {
41+
unmount(component)
42+
}
43+
}
44+
}
45+
46+
const instance = new Svelte5TestingLibrary()
47+
48+
export const render = instance.render.bind(instance)
49+
export const cleanup = instance.cleanup.bind(instance)

0 commit comments

Comments
 (0)