From e10c11aa1a3f39b1cbe6a317855ea82a8b0e1581 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 28 Feb 2024 11:38:12 -0500 Subject: [PATCH 1/3] implement inheritance with classes --- src/pure.js | 158 +++++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 76 deletions(-) diff --git a/src/pure.js b/src/pure.js index 3c412da..18dd633 100644 --- a/src/pure.js +++ b/src/pure.js @@ -7,85 +7,61 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler' import * as Svelte from 'svelte' const IS_SVELTE_5 = /^5\./.test(SVELTE_VERSION) -export const targetCache = new Set() -export const componentCache = new Set() - -const svelteComponentOptions = [ - 'accessors', - 'anchor', - 'props', - 'hydrate', - 'intro', - 'context', -] - -export const buildCheckProps = (svelteComponentOptions) => (options) => { - const isProps = !Object.keys(options).some((option) => - svelteComponentOptions.includes(option) - ) - - // Check if any props and Svelte options were accidentally mixed. - if (!isProps) { - const unrecognizedOptions = Object.keys(options).filter( - (option) => !svelteComponentOptions.includes(option) + +class SvelteTestingLibrary { + svelteComponentOptions = [ + 'accessors', + 'anchor', + 'props', + 'hydrate', + 'intro', + 'context', + ] + + targetCache = new Set() + componentCache = new Set() + + checkProps(options) { + const isProps = !Object.keys(options).some((option) => + this.svelteComponentOptions.includes(option) ) - if (unrecognizedOptions.length > 0) { - throw Error(` + // Check if any props and Svelte options were accidentally mixed. + if (!isProps) { + const unrecognizedOptions = Object.keys(options).filter( + (option) => !this.svelteComponentOptions.includes(option) + ) + + if (unrecognizedOptions.length > 0) { + throw Error(` Unknown options were found [${unrecognizedOptions}]. This might happen if you've mixed passing in props with Svelte options into the render function. Valid Svelte options - are [${svelteComponentOptions}]. You can either change the prop names, or pass in your + are [${this.svelteComponentOptions}]. You can either change the prop names, or pass in your props for that component via the \`props\` option.\n\n Eg: const { /** Results **/ } = render(MyComponent, { props: { /** props here **/ } })\n\n `) - } - - return options - } - - return { props: options } -} + } -const checkProps = buildCheckProps(svelteComponentOptions) - -const buildRenderComponent = - ({ target, ComponentConstructor }) => - (options) => { - options = { target, ...checkProps(options) } - - if (IS_SVELTE_5) - throw new Error('for Svelte 5, use `@testing-library/svelte/svelte5`') - - const component = new ComponentConstructor(options) - - componentCache.add(component) - - // TODO(mcous, 2024-02-11): remove this behavior in the next major version - // It is unnecessary has no path to implementation in Svelte v5 - if (!IS_SVELTE_5) { - component.$$.on_destroy.push(() => { - componentCache.delete(component) - }) + return options } - return component + return { props: options } } -export const buildRender = - (buildRenderComponent) => - (Component, { target, ...options } = {}, { container, queries } = {}) => { + render(Component, { target, ...options } = {}, { container, queries } = {}) { container = container || document.body target = target || container.appendChild(document.createElement('div')) - targetCache.add(target) + this.targetCache.add(target) const ComponentConstructor = Component.default || Component - const renderComponent = buildRenderComponent({ - target, - ComponentConstructor, - }) - - let component = renderComponent(options) + const component = this.renderComponent( + { + target, + ComponentConstructor, + }, + options + ) return { container, @@ -108,29 +84,59 @@ export const buildRender = } } -export const render = buildRender(buildRenderComponent) + renderComponent({ target, ComponentConstructor }, options) { + options = { target, ...this.checkProps(options) } + + if (IS_SVELTE_5) + throw new Error('for Svelte 5, use `@testing-library/svelte/svelte5`') + + const component = new ComponentConstructor(options) -export const cleanupComponent = (component) => { - const inCache = componentCache.delete(component) + this.componentCache.add(component) - if (inCache) { - component.$destroy() + // TODO(mcous, 2024-02-11): remove this behavior in the next major version + // It is unnecessary has no path to implementation in Svelte v5 + if (!IS_SVELTE_5) { + component.$$.on_destroy.push(() => { + this.componentCache.delete(component) + }) + } + + return component } -} -const cleanupTarget = (target) => { - const inCache = targetCache.delete(target) + cleanupComponent(component) { + const inCache = this.componentCache.delete(component) - if (inCache && target.parentNode === document.body) { - document.body.removeChild(target) + if (inCache) { + component.$destroy() + } + } + + cleanupTarget(target) { + const inCache = this.targetCache.delete(target) + + if (inCache && target.parentNode === document.body) { + document.body.removeChild(target) + } } -} -export const cleanup = () => { - componentCache.forEach(cleanupComponent) - targetCache.forEach(cleanupTarget) + cleanup() { + this.componentCache.forEach(cleanupComponent) + this.targetCache.forEach(cleanupTarget) + } } +const instance = new SvelteTestingLibrary() + +export const render = instance.render.bind(instance) + +export const cleanupComponent = instance.cleanupComponent.bind(instance) + +const cleanupTarget = instance.cleanupTarget.bind(instance) + +export const cleanup = instance.cleanup.bind(instance) + export const act = async (fn) => { if (fn) { await fn() From 1df1c7ca7a9af12f303a0f45595e587e3cbb4b96 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 28 Feb 2024 11:57:54 -0500 Subject: [PATCH 2/3] support for svelte 5 --- src/pure.js | 12 ++++-------- src/svelte5.js | 44 +++++++++++++++++--------------------------- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/pure.js b/src/pure.js index 18dd633..cb90733 100644 --- a/src/pure.js +++ b/src/pure.js @@ -8,7 +8,7 @@ import * as Svelte from 'svelte' const IS_SVELTE_5 = /^5\./.test(SVELTE_VERSION) -class SvelteTestingLibrary { +export class SvelteTestingLibrary { svelteComponentOptions = [ 'accessors', 'anchor', @@ -78,7 +78,7 @@ class SvelteTestingLibrary { await Svelte.tick() }, unmount: () => { - cleanupComponent(component) + this.cleanupComponent(component) }, ...getQueriesForElement(container, queries), } @@ -122,8 +122,8 @@ class SvelteTestingLibrary { } cleanup() { - this.componentCache.forEach(cleanupComponent) - this.targetCache.forEach(cleanupTarget) + this.componentCache.forEach(this.cleanupComponent.bind(this)) + this.targetCache.forEach(this.cleanupTarget.bind(this)) } } @@ -131,10 +131,6 @@ const instance = new SvelteTestingLibrary() export const render = instance.render.bind(instance) -export const cleanupComponent = instance.cleanupComponent.bind(instance) - -const cleanupTarget = instance.cleanupTarget.bind(instance) - export const cleanup = instance.cleanup.bind(instance) export const act = async (fn) => { diff --git a/src/svelte5.js b/src/svelte5.js index 6f30784..57e9e54 100644 --- a/src/svelte5.js +++ b/src/svelte5.js @@ -1,41 +1,31 @@ import { createClassComponent } from 'svelte/legacy' -import { - componentCache, - cleanup, - buildCheckProps, - buildRender, -} from './pure.js' +import { SvelteTestingLibrary } from './pure.js' -const svelteComponentOptions = [ - 'target', - 'props', - 'events', - 'context', - 'intro', - 'recover', -] +class Svelte5TestingLibrary extends SvelteTestingLibrary { + svelteComponentOptions = [ + 'target', + 'props', + 'events', + 'context', + 'intro', + 'recover', + ] -const checkProps = buildCheckProps(svelteComponentOptions) - -const buildRenderComponent = - ({ target, ComponentConstructor }) => - (options) => { - options = { target, ...checkProps(options) } + renderComponent({ target, ComponentConstructor }, options) { + options = { target, ...this.checkProps(options) } const component = createClassComponent({ component: ComponentConstructor, ...options, }) - componentCache.add(component) + this.componentCache.add(component) return component } +} -const render = buildRender(buildRenderComponent) - -/* eslint-disable import/export */ - -import { act, fireEvent } from './pure.js' +const instance = new Svelte5TestingLibrary() -export { render, cleanup, fireEvent, act } +export const render = instance.render.bind(instance) +export const cleanup = instance.cleanup.bind(instance) From 07c546b03db86a05e859b6b12532b1f90733bdb5 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Wed, 28 Feb 2024 14:51:01 -0500 Subject: [PATCH 3/3] dev dependency of any of the Sveltes --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e8c6f1..6ee42a8 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "npm-run-all": "^4.1.5", "prettier": "3.2.4", "prettier-plugin-svelte": "3.1.2", - "svelte": "^4.2.10", + "svelte": "^3 || ^4 || ^5", "svelte-check": "^3.6.3", "svelte-jester": "^3.0.0", "typescript": "^5.3.3",