diff --git a/src/__tests__/fixtures/Rerender.svelte b/src/__tests__/fixtures/Rerender.svelte new file mode 100644 index 0000000..aac1600 --- /dev/null +++ b/src/__tests__/fixtures/Rerender.svelte @@ -0,0 +1,15 @@ + + +

Hello {name}!

+ +
{$mountCounter}
diff --git a/src/__tests__/rerender.test.js b/src/__tests__/rerender.test.js index e3eddb2..d6cbc21 100644 --- a/src/__tests__/rerender.test.js +++ b/src/__tests__/rerender.test.js @@ -1,38 +1,38 @@ /** * @jest-environment jsdom */ -import { describe, expect, test } from 'vitest' +import { expect, test, vi } from 'vitest' +import { writable } from 'svelte/store' -import { render } from '..' -import Comp from './fixtures/Comp.svelte' +import { render, waitFor } from '..' +import Comp from './fixtures/Rerender.svelte' -describe('rerender', () => { - test('mounts new component successfully', () => { - const { container, rerender } = render(Comp, { props: { name: 'World 1' } }) +const mountCounter = writable(0) - expect(container.firstChild).toHaveTextContent('Hello World 1!') - rerender({ props: { name: 'World 2' } }) - expect(container.firstChild).toHaveTextContent('Hello World 2!') +test('mounts new component successfully', async () => { + const { getByTestId, rerender } = render(Comp, { + props: { name: 'World 1' }, + context: new Map(Object.entries({ mountCounter })), }) - test('destroys old component', () => { - let isDestroyed + const expectToRender = (content) => + waitFor(() => { + expect(getByTestId('test')).toHaveTextContent(content) + expect(getByTestId('mount-counter')).toHaveTextContent('1') + }) - const { rerender, component } = render(Comp, { props: { name: '' } }) + await expectToRender('Hello World 1!') - component.$$.on_destroy.push(() => { - isDestroyed = true - }) - rerender({ props: { name: '' } }) - expect(isDestroyed).toBeTruthy() - }) + console.warn = vi.fn() - test('destroys old components on multiple rerenders', () => { - const { rerender, queryByText } = render(Comp, { props: { name: 'Neil' } }) + rerender({ props: { name: 'World 2' } }) + await expectToRender('Hello World 2!') - rerender({ props: { name: 'Alex' } }) - expect(queryByText('Hello Neil!')).not.toBeInTheDocument() - rerender({ props: { name: 'Geddy' } }) - expect(queryByText('Hello Alex!')).not.toBeInTheDocument() - }) + expect(console.warn).toHaveBeenCalled() + + console.warn.mockClear() + rerender({ name: 'World 3' }) + await expectToRender('Hello World 3!') + + expect(console.warn).not.toHaveBeenCalled() }) diff --git a/src/pure.js b/src/pure.js index 6d49434..59c62ff 100644 --- a/src/pure.js +++ b/src/pure.js @@ -1,7 +1,7 @@ import { fireEvent as dtlFireEvent, getQueriesForElement, - prettyDOM + prettyDOM, } from '@testing-library/dom' import { tick } from 'svelte' @@ -14,7 +14,7 @@ const svelteComponentOptions = [ 'props', 'hydrate', 'intro', - 'context' + 'context', ] const render = ( @@ -56,7 +56,7 @@ const render = ( let component = new ComponentConstructor({ target, - ...checkProps(options) + ...checkProps(options), }) containerCache.add({ container, target, component }) @@ -70,26 +70,20 @@ const render = ( container, component, debug: (el = container) => console.log(prettyDOM(el)), - rerender: (options) => { - if (componentCache.has(component)) component.$destroy() - - // eslint-disable-next-line no-new - component = new ComponentConstructor({ - target, - ...checkProps(options) - }) - - containerCache.add({ container, target, component }) - componentCache.add(component) - - component.$$.on_destroy.push(() => { - componentCache.delete(component) - }) + rerender: async (props) => { + if (props.props) { + console.warn( + 'rerender({ props: {...} }) deprecated, use rerender({...}) instead' + ) + props = props.props + } + component.$set(props) + await tick() }, unmount: () => { if (componentCache.has(component)) component.$destroy() }, - ...getQueriesForElement(container, queries) + ...getQueriesForElement(container, queries), } } diff --git a/types/index.d.ts b/types/index.d.ts index 26d85d7..d60d779 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2,25 +2,42 @@ // Project: https://github.com/testing-library/svelte-testing-library // Definitions by: Rahim Alwer -import {BoundFunction, EventType,Queries, queries} from '@testing-library/dom' -import { ComponentConstructorOptions,ComponentProps, SvelteComponent } from 'svelte' +import { + BoundFunction, + EventType, + Queries, + queries, +} from '@testing-library/dom' +import { + ComponentConstructorOptions, + ComponentProps, + SvelteComponent, +} from 'svelte' export * from '@testing-library/dom' -type SvelteComponentOptions = ComponentProps | Pick>, "anchor" | "props" | "hydrate" | "intro" | "context"> +type SvelteComponentOptions = + | ComponentProps + | Pick< + ComponentConstructorOptions>, + 'anchor' | 'props' | 'hydrate' | 'intro' | 'context' + > type Omit = Pick> -type Constructor = new (...args: any[]) => T; +type Constructor = new (...args: any[]) => T /** * Render a Component into the Document. */ -export type RenderResult = { +export type RenderResult< + C extends SvelteComponent, + Q extends Queries = typeof queries, +> = { container: HTMLElement component: C debug: (el?: HTMLElement | DocumentFragment) => void - rerender: (options: SvelteComponentOptions) => void + rerender: (props: ComponentProps) => Promise unmount: () => void } & { [P in keyof Q]: BoundFunction } @@ -38,7 +55,7 @@ export function render( export function render( component: Constructor, componentOptions?: SvelteComponentOptions, - renderOptions?: RenderOptions, + renderOptions?: RenderOptions ): RenderResult /** @@ -50,13 +67,19 @@ export function cleanup(): void * Fires DOM events on an element provided by @testing-library/dom. Since Svelte needs to flush * pending state changes via `tick`, these methods have been override and now return a promise. */ -export type FireFunction = (element: Document | Element | Window, event: Event) => Promise; +export type FireFunction = ( + element: Document | Element | Window, + event: Event +) => Promise export type FireObject = { - [K in EventType]: (element: Document | Element | Window, options?: {}) => Promise; -}; + [K in EventType]: ( + element: Document | Element | Window, + options?: {} + ) => Promise +} -export const fireEvent: FireFunction & FireObject; +export const fireEvent: FireFunction & FireObject /** * Calls a function and notifies Svelte to flush any pending state changes.