Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 10f0221

Browse files
committed
fix: configure dom-testing-library to flush Svelte changes
1 parent 1aa3701 commit 10f0221

File tree

10 files changed

+133
-42
lines changed

10 files changed

+133
-42
lines changed

‎README.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
- [This Solution](#this-solution)
7171
- [Installation](#installation)
7272
- [Setup](#setup)
73+
- [Auto-cleanup](#auto-cleanup)
7374
- [Docs](#docs)
7475
- [Issues](#issues)
7576
- [🐛 Bugs](#-bugs)
@@ -140,6 +141,39 @@ test runners like Jest.
140141
[vitest]: https://vitest.dev/
141142
[setup docs]: https://testing-library.com/docs/svelte-testing-library/setup
142143

144+
### Auto-cleanup
145+
146+
In Vitest (via the `svelteTesting` plugin) and Jest (via the `beforeEach` and `afterEach` globals),
147+
this library will automatically setup and cleanup the test environment before and after each test.
148+
149+
To do your own cleanup, or if you're using another framework, call the `setup` and `cleanup` functions yourself:
150+
151+
```js
152+
import { cleanup, render, setup } from '@testing-library/svelte'
153+
154+
// before
155+
setup()
156+
157+
// test
158+
render(/* ... */)
159+
160+
// after
161+
cleanup()
162+
```
163+
164+
To disable auto-cleanup in Vitest, set the `autoCleanup` option of the plugin to false:
165+
166+
```js
167+
svelteTesting({ autoCleanup: false })
168+
```
169+
170+
To disable auto-cleanup in Jest and other frameworks with global test hooks,
171+
set the `STL_SKIP_AUTO_CLEANUP` environment variable:
172+
173+
```shell
174+
STL_SKIP_AUTO_CLEANUP=1 jest
175+
```
176+
143177
## Docs
144178

145179
See the [**docs**][stl-docs] over at the Testing Library website.

‎jest.config.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default {
1414
extensionsToTreatAsEsm: ['.svelte'],
1515
testEnvironment: 'jsdom',
1616
setupFilesAfterEnv: ['<rootDir>/tests/_jest-setup.js'],
17-
injectGlobals: false,
17+
injectGlobals: true,
1818
moduleNameMapper: {
1919
'^vitest$': '<rootDir>/tests/_jest-vitest-alias.js',
2020
[String.raw`^@testing-library\/svelte$`]: '<rootDir>/src/index.js',

‎src/index.js‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
import { act, cleanup } from './pure.js'
1+
import { act, cleanup,setup } from './pure.js'
22

3-
// If we're running in a test runner that supports afterEach
4-
// then we'll automatically run cleanup afterEach test
3+
// If we're running in a test runner that supports beforeEach/afterEach
4+
// we'll automatically run setup and cleanup before and after each test
55
// this ensures that tests run in isolation from each other
66
// if you don't like this then set the STL_SKIP_AUTO_CLEANUP env variable.
7-
if (typeof afterEach === 'function' && !process.env.STL_SKIP_AUTO_CLEANUP) {
8-
afterEach(async () => {
9-
await act()
10-
cleanup()
11-
})
7+
if (typeof process !== 'undefined' && !process.env.STL_SKIP_AUTO_CLEANUP) {
8+
if (typeof beforeEach === 'function') {
9+
beforeEach(() => {
10+
setup()
11+
})
12+
}
13+
14+
if (typeof afterEach === 'function') {
15+
afterEach(async () => {
16+
await act()
17+
cleanup()
18+
})
19+
}
1220
}
1321

1422
// export all base queries, screen, etc.

‎src/pure.js‎

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {
2+
configure as configureDTL,
23
fireEvent as baseFireEvent,
4+
getConfig as getDTLConfig,
35
getQueriesForElement,
46
prettyDOM,
57
} from '@testing-library/dom'
6-
import {tick} from 'svelte'
8+
import *asSvelte from 'svelte'
79

810
import { mount, unmount, updateProps, validateOptions } from './core/index.js'
911

@@ -94,7 +96,7 @@ const render = (Component, options = {}, renderOptions = {}) => {
9496
}
9597

9698
updateProps(component, props)
97-
await tick()
99+
await Svelte.tick()
98100
},
99101
unmount: () => {
100102
cleanupComponent(component)
@@ -103,6 +105,33 @@ const render = (Component, options = {}, renderOptions = {}) => {
103105
}
104106
}
105107

108+
/** @type {import('@testing-library/dom'.Config | undefined} */
109+
let originalDTLConfig
110+
111+
/**
112+
* Configure `@testing-library/dom` for usage with Svelte.
113+
*
114+
* Ensures events fired from `@testing-library/dom`
115+
* and `@testing-library/user-event` wait for Svelte
116+
* to flush changes to the DOM before proceeding.
117+
*/
118+
const setup = () => {
119+
originalDTLConfig = getDTLConfig()
120+
121+
configureDTL({
122+
asyncWrapper: act,
123+
eventWrapper: Svelte.flushSync ?? ((cb) => cb()),
124+
})
125+
}
126+
127+
/** Reset dom-testing-library config. */
128+
const cleanupDTL = () => {
129+
if (originalDTLConfig) {
130+
configureDTL(originalDTLConfig)
131+
originalDTLConfig = undefined
132+
}
133+
}
134+
106135
/** Remove a component from the component cache. */
107136
const cleanupComponent = (component) => {
108137
const inCache = componentCache.delete(component)
@@ -121,27 +150,31 @@ const cleanupTarget = (target) => {
121150
}
122151
}
123152

124-
/** Unmount all components and remove elements added to `<body>`. */
153+
/** Unmount components, remove elements added to `<body>`, and reset `@testing-library/dom`. */
125154
const cleanup = () => {
126155
for (const component of componentCache) {
127156
cleanupComponent(component)
128157
}
129158
for (const target of targetCache) {
130159
cleanupTarget(target)
131160
}
161+
cleanupDTL()
132162
}
133163

134164
/**
135165
* Call a function and wait for Svelte to flush pending changes.
136166
*
137-
* @param {() => unknown} [fn] - A function, which may be `async`, to call before flushing updates.
138-
* @returns {Promise<void>}
167+
* @template T
168+
* @param {(() => Promise<T>) | () => T} [fn] - A function, which may be `async`, to call before flushing updates.
169+
* @returns {Promise<T>}
139170
*/
140171
const act = async (fn) => {
172+
let result
141173
if (fn) {
142-
await fn()
174+
result=await fn()
143175
}
144-
return tick()
176+
await Svelte.tick()
177+
return result
145178
}
146179

147180
/**
@@ -162,18 +195,10 @@ const act = async (fn) => {
162195
*
163196
* @type {FireFunction & FireObject}
164197
*/
165-
const fireEvent = async (...args) => {
166-
const event = baseFireEvent(...args)
167-
await tick()
168-
return event
169-
}
198+
const fireEvent = async (...args) => act(() => baseFireEvent(...args))
170199

171200
for (const [key, baseEvent] of Object.entries(baseFireEvent)) {
172-
fireEvent[key] = async (...args) => {
173-
const event = baseEvent(...args)
174-
await tick()
175-
return event
176-
}
201+
fireEvent[key] = async (...args) => act(() => baseEvent(...args))
177202
}
178203

179-
export { act, cleanup, fireEvent, render }
204+
export { act, cleanup, fireEvent, render,setup }

‎src/vitest.js‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import { act, cleanup } from '@testing-library/svelte'
2-
import { afterEach } from 'vitest'
1+
import { act, cleanup,setup } from '@testing-library/svelte'
2+
import { beforeEach } from 'vitest'
33

4-
afterEach(async () => {
5-
await act()
6-
cleanup()
4+
beforeEach(() => {
5+
setup()
6+
7+
return async () => {
8+
await act()
9+
cleanup()
10+
}
711
})

‎tests/_jest-setup.js‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1 @@
11
import '@testing-library/jest-dom/jest-globals'
2-
3-
import { afterEach } from '@jest/globals'
4-
import { act, cleanup } from '@testing-library/svelte'
5-
6-
afterEach(async () => {
7-
await act()
8-
cleanup()
9-
})

‎tests/_jest-vitest-alias.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ export {
1111
jest as vi,
1212
} from '@jest/globals'
1313

14-
// Add support for describe.skipIfand test.skipIf
14+
// Add support for describe.skipIf, test.skipIf, and test.runIf
1515
describe.skipIf = (condition) => (condition ? describe.skip : describe)
1616
test.skipIf = (condition) => (condition ? test.skip : test)
17+
test.runIf = (condition) => (condition ? test : test.skip)
1718

1819
// Add support for `stubGlobal`
1920
jest.stubGlobal = (property, stub) => {

‎tests/act.test.js‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { setTimeout } from 'node:timers/promises'
22

33
import { act, render, screen } from '@testing-library/svelte'
4+
import { userEvent } from '@testing-library/user-event'
45
import { describe, expect, test } from 'vitest'
56

67
import Comp from './fixtures/Comp.svelte'
@@ -24,10 +25,20 @@ describe('act', () => {
2425
const button = screen.getByText('Button')
2526

2627
await act(async () => {
27-
await setTimeout(100)
28+
await setTimeout(10)
2829
button.click()
2930
})
3031

3132
expect(button).toHaveTextContent('Button Clicked')
3233
})
34+
35+
test('wires act into user-event', async () => {
36+
const user = userEvent.setup()
37+
render(Comp)
38+
const button = screen.getByText('Button')
39+
40+
await user.click(button)
41+
42+
expect(button).toHaveTextContent('Button Clicked')
43+
})
3344
})

‎tests/auto-cleanup.test.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ import { IS_JEST } from './_env.js'
66
// in Jest breaks Svelte's environment checking heuristics.
77
// Re-implement this test in a more accurate environment, without mocks.
88
describe.skipIf(IS_JEST)('auto-cleanup', () => {
9+
const globalBeforeEach = vi.fn()
910
const globalAfterEach = vi.fn()
1011

1112
beforeEach(() => {
1213
vi.resetModules()
14+
globalThis.beforeEach = globalBeforeEach
1315
globalThis.afterEach = globalAfterEach
1416
})
1517

1618
afterEach(() => {
1719
delete process.env.STL_SKIP_AUTO_CLEANUP
20+
delete globalThis.beforeEach
1821
delete globalThis.afterEach
1922
})
2023

@@ -37,6 +40,7 @@ describe.skipIf(IS_JEST)('auto-cleanup', () => {
3740

3841
await import('@testing-library/svelte')
3942

43+
expect(globalBeforeEach).toHaveBeenCalledTimes(0)
4044
expect(globalAfterEach).toHaveBeenCalledTimes(0)
4145
})
4246
})

‎tests/events.test.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { fireEvent as fireEventDTL } from '@testing-library/dom'
12
import { fireEvent, render, screen } from '@testing-library/svelte'
23
import { describe, expect, test } from 'vitest'
34

5+
import { IS_SVELTE_5 } from './_env.js'
46
import Comp from './fixtures/Comp.svelte'
57

68
describe('events', () => {
@@ -29,4 +31,14 @@ describe('events', () => {
2931
await expect(result).resolves.toBe(true)
3032
expect(button).toHaveTextContent('Button Clicked')
3133
})
34+
35+
test.runIf(IS_SVELTE_5)('state changes are flushed synchronously', () => {
36+
render(Comp, { props: { name: 'World' } })
37+
const button = screen.getByText('Button')
38+
39+
const result = fireEventDTL.click(button)
40+
41+
expect(result).toBe(true)
42+
expect(button).toHaveTextContent('Button Clicked')
43+
})
3244
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /