Skip to content

Commit 0593819

Browse files
authored
refactor: implement inheritance with classes (#330)
1 parent 40973c5 commit 0593819

File tree

3 files changed

+97
-105
lines changed

3 files changed

+97
-105
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
"npm-run-all": "^4.1.5",
9999
"prettier": "3.2.4",
100100
"prettier-plugin-svelte": "3.1.2",
101-
"svelte": "^4.2.10",
101+
"svelte": "^3 || ^4 || ^5",
102102
"svelte-check": "^3.6.3",
103103
"svelte-jester": "^3.0.0",
104104
"typescript": "^5.3.3",

src/pure.js

+79-77
Original file line numberDiff line numberDiff line change
@@ -7,85 +7,61 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
77
import * as Svelte from 'svelte'
88

99
const IS_SVELTE_5 = /^5\./.test(SVELTE_VERSION)
10-
export const targetCache = new Set()
11-
export const componentCache = new Set()
12-
13-
const svelteComponentOptions = [
14-
'accessors',
15-
'anchor',
16-
'props',
17-
'hydrate',
18-
'intro',
19-
'context',
20-
]
21-
22-
export const buildCheckProps = (svelteComponentOptions) => (options) => {
23-
const isProps = !Object.keys(options).some((option) =>
24-
svelteComponentOptions.includes(option)
25-
)
26-
27-
// Check if any props and Svelte options were accidentally mixed.
28-
if (!isProps) {
29-
const unrecognizedOptions = Object.keys(options).filter(
30-
(option) => !svelteComponentOptions.includes(option)
10+
11+
export class SvelteTestingLibrary {
12+
svelteComponentOptions = [
13+
'accessors',
14+
'anchor',
15+
'props',
16+
'hydrate',
17+
'intro',
18+
'context',
19+
]
20+
21+
targetCache = new Set()
22+
componentCache = new Set()
23+
24+
checkProps(options) {
25+
const isProps = !Object.keys(options).some((option) =>
26+
this.svelteComponentOptions.includes(option)
3127
)
3228

33-
if (unrecognizedOptions.length > 0) {
34-
throw Error(`
29+
// Check if any props and Svelte options were accidentally mixed.
30+
if (!isProps) {
31+
const unrecognizedOptions = Object.keys(options).filter(
32+
(option) => !this.svelteComponentOptions.includes(option)
33+
)
34+
35+
if (unrecognizedOptions.length > 0) {
36+
throw Error(`
3537
Unknown options were found [${unrecognizedOptions}]. This might happen if you've mixed
3638
passing in props with Svelte options into the render function. Valid Svelte options
37-
are [${svelteComponentOptions}]. You can either change the prop names, or pass in your
39+
are [${this.svelteComponentOptions}]. You can either change the prop names, or pass in your
3840
props for that component via the \`props\` option.\n\n
3941
Eg: const { /** Results **/ } = render(MyComponent, { props: { /** props here **/ } })\n\n
4042
`)
41-
}
42-
43-
return options
44-
}
45-
46-
return { props: options }
47-
}
48-
49-
const checkProps = buildCheckProps(svelteComponentOptions)
50-
51-
const buildRenderComponent =
52-
({ target, ComponentConstructor }) =>
53-
(options) => {
54-
options = { target, ...checkProps(options) }
55-
56-
if (IS_SVELTE_5)
57-
throw new Error('for Svelte 5, use `@testing-library/svelte/svelte5`')
43+
}
5844

59-
const component = new ComponentConstructor(options)
60-
61-
componentCache.add(component)
62-
63-
// TODO(mcous, 2024-02-11): remove this behavior in the next major version
64-
// It is unnecessary has no path to implementation in Svelte v5
65-
if (!IS_SVELTE_5) {
66-
component.$$.on_destroy.push(() => {
67-
componentCache.delete(component)
68-
})
45+
return options
6946
}
7047

71-
return component
48+
return { props: options }
7249
}
7350

74-
export const buildRender =
75-
(buildRenderComponent) =>
76-
(Component, { target, ...options } = {}, { container, queries } = {}) => {
51+
render(Component, { target, ...options } = {}, { container, queries } = {}) {
7752
container = container || document.body
7853
target = target || container.appendChild(document.createElement('div'))
79-
targetCache.add(target)
54+
this.targetCache.add(target)
8055

8156
const ComponentConstructor = Component.default || Component
8257

83-
const renderComponent = buildRenderComponent({
84-
target,
85-
ComponentConstructor,
86-
})
87-
88-
let component = renderComponent(options)
58+
const component = this.renderComponent(
59+
{
60+
target,
61+
ComponentConstructor,
62+
},
63+
options
64+
)
8965

9066
return {
9167
container,
@@ -102,35 +78,61 @@ export const buildRender =
10278
await Svelte.tick()
10379
},
10480
unmount: () => {
105-
cleanupComponent(component)
81+
this.cleanupComponent(component)
10682
},
10783
...getQueriesForElement(container, queries),
10884
}
10985
}
11086

111-
export const render = buildRender(buildRenderComponent)
87+
renderComponent({ target, ComponentConstructor }, options) {
88+
options = { target, ...this.checkProps(options) }
11289

113-
export const cleanupComponent = (component) => {
114-
const inCache = componentCache.delete(component)
90+
if (IS_SVELTE_5)
91+
throw new Error('for Svelte 5, use `@testing-library/svelte/svelte5`')
92+
93+
const component = new ComponentConstructor(options)
94+
95+
this.componentCache.add(component)
96+
97+
// TODO(mcous, 2024-02-11): remove this behavior in the next major version
98+
// It is unnecessary has no path to implementation in Svelte v5
99+
if (!IS_SVELTE_5) {
100+
component.$$.on_destroy.push(() => {
101+
this.componentCache.delete(component)
102+
})
103+
}
115104

116-
if (inCache) {
117-
component.$destroy()
105+
return component
118106
}
119-
}
120107

121-
const cleanupTarget = (target) => {
122-
const inCache = targetCache.delete(target)
108+
cleanupComponent(component) {
109+
const inCache = this.componentCache.delete(component)
123110

124-
if (inCache && target.parentNode === document.body) {
125-
document.body.removeChild(target)
111+
if (inCache) {
112+
component.$destroy()
113+
}
114+
}
115+
116+
cleanupTarget(target) {
117+
const inCache = this.targetCache.delete(target)
118+
119+
if (inCache && target.parentNode === document.body) {
120+
document.body.removeChild(target)
121+
}
126122
}
127-
}
128123

129-
export const cleanup = () => {
130-
componentCache.forEach(cleanupComponent)
131-
targetCache.forEach(cleanupTarget)
124+
cleanup() {
125+
this.componentCache.forEach(this.cleanupComponent.bind(this))
126+
this.targetCache.forEach(this.cleanupTarget.bind(this))
127+
}
132128
}
133129

130+
const instance = new SvelteTestingLibrary()
131+
132+
export const render = instance.render.bind(instance)
133+
134+
export const cleanup = instance.cleanup.bind(instance)
135+
134136
export const act = async (fn) => {
135137
if (fn) {
136138
await fn()

src/svelte5.js

+17-27
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
11
import { createClassComponent } from 'svelte/legacy'
2-
import {
3-
componentCache,
4-
cleanup,
5-
buildCheckProps,
6-
buildRender,
7-
} from './pure.js'
2+
import { SvelteTestingLibrary } from './pure.js'
83

9-
const svelteComponentOptions = [
10-
'target',
11-
'props',
12-
'events',
13-
'context',
14-
'intro',
15-
'recover',
16-
]
4+
class Svelte5TestingLibrary extends SvelteTestingLibrary {
5+
svelteComponentOptions = [
6+
'target',
7+
'props',
8+
'events',
9+
'context',
10+
'intro',
11+
'recover',
12+
]
1713

18-
const checkProps = buildCheckProps(svelteComponentOptions)
19-
20-
const buildRenderComponent =
21-
({ target, ComponentConstructor }) =>
22-
(options) => {
23-
options = { target, ...checkProps(options) }
14+
renderComponent({ target, ComponentConstructor }, options) {
15+
options = { target, ...this.checkProps(options) }
2416

2517
const component = createClassComponent({
2618
component: ComponentConstructor,
2719
...options,
2820
})
2921

30-
componentCache.add(component)
22+
this.componentCache.add(component)
3123

3224
return component
3325
}
26+
}
3427

35-
const render = buildRender(buildRenderComponent)
36-
37-
/* eslint-disable import/export */
38-
39-
import { act, fireEvent } from './pure.js'
28+
const instance = new Svelte5TestingLibrary()
4029

41-
export { render, cleanup, fireEvent, act }
30+
export const render = instance.render.bind(instance)
31+
export const cleanup = instance.cleanup.bind(instance)

0 commit comments

Comments
 (0)