diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 778d507..326785a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -25,6 +25,7 @@ module.exports = { }, rules: { 'no-undef-init': 'off', + 'prefer-const': 'off', }, }, { @@ -49,5 +50,6 @@ module.exports = { ecmaVersion: 2022, sourceType: 'module', }, + globals: { $state: 'readonly', $props: 'readonly' }, ignorePatterns: ['!/.*'], } diff --git a/src/__tests__/fixtures/Comp.svelte b/src/__tests__/fixtures/Comp.svelte index ba23d88..18365a4 100644 --- a/src/__tests__/fixtures/Comp.svelte +++ b/src/__tests__/fixtures/Comp.svelte @@ -13,5 +13,3 @@

Hello {name}!

- - diff --git a/src/__tests__/fixtures/CompRunes.svelte b/src/__tests__/fixtures/CompRunes.svelte new file mode 100644 index 0000000..77646e3 --- /dev/null +++ b/src/__tests__/fixtures/CompRunes.svelte @@ -0,0 +1,13 @@ + + +

Hello {name}!

+ + diff --git a/src/__tests__/render.test.js b/src/__tests__/render.test.js index ea445d5..92fda6d 100644 --- a/src/__tests__/render.test.js +++ b/src/__tests__/render.test.js @@ -1,11 +1,15 @@ import { render } from '@testing-library/svelte' -import { describe, expect, test } from 'vitest' +import { beforeAll, describe, expect, test } from 'vitest' -import Comp from './fixtures/Comp.svelte' -import { IS_SVELTE_5 } from './utils.js' +import { COMPONENT_FIXTURES, IS_SVELTE_5 } from './utils.js' -describe('render', () => { +describe.each(COMPONENT_FIXTURES)('render $name', ({ component }) => { const props = { name: 'World' } + let Comp + + beforeAll(async () => { + Comp = await import(component) + }) test('renders component into the document', () => { const { getByText } = render(Comp, { props }) diff --git a/src/__tests__/rerender.test.js b/src/__tests__/rerender.test.js index ca4b8e8..0cc0941 100644 --- a/src/__tests__/rerender.test.js +++ b/src/__tests__/rerender.test.js @@ -1,10 +1,15 @@ import { act, render, screen } from '@testing-library/svelte' -import { VERSION as SVELTE_VERSION } from 'svelte/compiler' -import { describe, expect, test, vi } from 'vitest' +import { beforeAll, describe, expect, test, vi } from 'vitest' -import Comp from './fixtures/Comp.svelte' +import { COMPONENT_FIXTURES, IS_SVELTE_5, TYPE_RUNES } from './utils.js' + +describe.each(COMPONENT_FIXTURES)('rerender $type', ({ type, component }) => { + let Comp + + beforeAll(async () => { + Comp = await import(component) + }) -describe('rerender', () => { test('updates props', async () => { const { rerender } = render(Comp, { name: 'World' }) const element = screen.getByText('Hello World!') @@ -29,13 +34,12 @@ describe('rerender', () => { ) }) - test('change props with accessors', async () => { - const { component, getByText } = render( - Comp, - SVELTE_VERSION < '5' - ? { accessors: true, props: { name: 'World' } } - : { name: 'World' } - ) + test.skipIf(type === TYPE_RUNES)('change props with accessors', async () => { + const componentOptions = IS_SVELTE_5 + ? { name: 'World' } + : { accessors: true, props: { name: 'World' } } + + const { component, getByText } = render(Comp, componentOptions) const element = getByText('Hello World!') expect(element).toBeInTheDocument() diff --git a/src/__tests__/utils.js b/src/__tests__/utils.js index 69be184..4fcefe0 100644 --- a/src/__tests__/utils.js +++ b/src/__tests__/utils.js @@ -5,3 +5,14 @@ export const IS_JSDOM = window.navigator.userAgent.includes('jsdom') export const IS_HAPPYDOM = !IS_JSDOM // right now it's happy or js export const IS_SVELTE_5 = SVELTE_VERSION >= '5' + +export const TYPE_LEGACY = 'legacy' + +export const TYPE_RUNES = 'runes' + +export const COMPONENT_FIXTURES = [ + { type: TYPE_LEGACY, component: './fixtures/Comp.svelte' }, + IS_SVELTE_5 + ? { type: TYPE_RUNES, component: './fixtures/CompRunes.svelte' } + : [], +].flat() diff --git a/src/pure.js b/src/pure.js index 364c225..d144cd5 100644 --- a/src/pure.js +++ b/src/pure.js @@ -80,7 +80,8 @@ export class SvelteTestingLibrary { ) props = props.props } - component.$set(props) + + this.rerenderComponent(component, props) await Svelte.tick() }, unmount: () => { @@ -107,6 +108,10 @@ export class SvelteTestingLibrary { return component } + rerenderComponent(component, nextProps) { + component.$set(nextProps) + } + cleanupComponent(component) { const inCache = this.componentCache.delete(component) diff --git a/src/svelte5-index.js b/src/svelte5-index.js index ab49641..96d9086 100644 --- a/src/svelte5-index.js +++ b/src/svelte5-index.js @@ -1,6 +1,6 @@ /* eslint-disable import/export */ import { act } from './pure.js' -import { cleanup } from './svelte5.js' +import { cleanup } from './svelte5.svelte.js' // If we're running in a test runner that supports afterEach // then we'll automatically run cleanup afterEach test @@ -20,4 +20,4 @@ export * from '@testing-library/dom' // export svelte-specific functions and custom `fireEvent` // `fireEvent` must be a named export to take priority over wildcard export above export { act, fireEvent } from './pure.js' -export { cleanup, render } from './svelte5.js' +export { cleanup, render } from './svelte5.svelte.js' diff --git a/src/svelte5.js b/src/svelte5.js deleted file mode 100644 index a8dd494..0000000 --- a/src/svelte5.js +++ /dev/null @@ -1,30 +0,0 @@ -import { createClassComponent } from 'svelte/legacy' - -import { SvelteTestingLibrary } from './pure.js' - -class Svelte5TestingLibrary extends SvelteTestingLibrary { - svelteComponentOptions = [ - 'target', - 'props', - 'events', - 'context', - 'intro', - 'recover', - ] - - renderComponent(ComponentConstructor, componentOptions) { - const component = createClassComponent({ - ...componentOptions, - component: ComponentConstructor, - }) - - this.componentCache.add(component) - - return component - } -} - -const instance = new Svelte5TestingLibrary() - -export const render = instance.render.bind(instance) -export const cleanup = instance.cleanup.bind(instance) diff --git a/src/svelte5.svelte.js b/src/svelte5.svelte.js new file mode 100644 index 0000000..b1bb2fc --- /dev/null +++ b/src/svelte5.svelte.js @@ -0,0 +1,49 @@ +/** eslint-global: $state */ +import { mount, unmount } from 'svelte' + +import { SvelteTestingLibrary } from './pure.js' + +class Svelte5TestingLibrary extends SvelteTestingLibrary { + svelteComponentOptions = [ + 'target', + 'props', + 'events', + 'context', + 'intro', + 'recover', + ] + + propsByComponent = new Map() + + renderComponent(ComponentConstructor, componentOptions) { + const props = $state(componentOptions.props ?? {}) + const component = mount(ComponentConstructor, { + ...componentOptions, + props, + }) + + this.componentCache.add(component) + this.propsByComponent.set(component, props) + + return component + } + + rerenderComponent(component, nextProps) { + const prevProps = this.propsByComponent.get(component) + Object.assign(prevProps, nextProps) + } + + cleanupComponent(component) { + const inCache = this.componentCache.delete(component) + this.propsByComponent.delete(component) + + if (inCache) { + unmount(component) + } + } +} + +const instance = new Svelte5TestingLibrary() + +export const render = instance.render.bind(instance) +export const cleanup = instance.cleanup.bind(instance)