@@ -7,92 +7,61 @@ import { VERSION as SVELTE_VERSION } from 'svelte/compiler'
7
7
import * as Svelte from 'svelte'
8
8
9
9
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 isOptions = Object . keys ( options ) . some ( ( option ) =>
24
- svelteComponentOptions . includes ( option )
25
- )
26
-
27
- // Check if any props and Svelte options were accidentally mixed.
28
- if ( isOptions ) {
29
- const unrecognizedOptions = Object . keys ( options ) . filter (
30
- ( option ) => ! svelteComponentOptions . includes ( option )
31
- )
32
-
33
- if ( unrecognizedOptions . length > 0 ) {
34
- throw Error ( `
35
- Unknown component options: [${ unrecognizedOptions . join ( ', ' ) } ]
36
- Valid Svelte component options: [${ svelteComponentOptions . join ( ', ' ) } ]
37
-
38
- This error occurs if props are mixed with Svelte component options,
39
- or any props use the same name as a Svelte component option.
40
- Either rename the props, or place props under the \`props\` option.
41
-
42
- Eg: const { /** results **/ } = render(MyComponent, { props: { /** props here **/ } })
43
- ` )
44
- }
45
-
46
- return options
47
- }
48
-
49
- return { props : options }
50
- }
51
-
52
- const checkProps = buildCheckProps ( svelteComponentOptions )
53
-
54
- const buildRenderComponent =
55
- ( { target, ComponentConstructor } ) =>
56
- ( options ) => {
57
- options = checkProps ( options )
58
-
59
- if ( IS_SVELTE_5 )
60
- throw new Error ( 'for Svelte 5, use `@testing-library/svelte/svelte5`' )
61
-
62
- const component = new ComponentConstructor ( options )
63
10
64
- componentCache . add ( component )
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 )
27
+ )
65
28
66
- // TODO(mcous, 2024-02-11): remove this behavior in the next major version
67
- // It is unnecessary has no path to implementation in Svelte v5
68
- if ( ! IS_SVELTE_5 ) {
69
- component . $$ . on_destroy . push ( ( ) => {
70
- componentCache . delete ( component )
71
- } )
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 ( `
37
+ Unknown options were found [${ unrecognizedOptions } ]. This might happen if you've mixed
38
+ passing in props with Svelte options into the render function. Valid Svelte options
39
+ are [${ this . svelteComponentOptions } ]. You can either change the prop names, or pass in your
40
+ props for that component via the \`props\` option.\n\n
41
+ Eg: const { /** Results **/ } = render(MyComponent, { props: { /** props here **/ } })\n\n
42
+ ` )
43
+ }
44
+
45
+ return options
72
46
}
73
47
74
- return component
48
+ return { props : options }
75
49
}
76
50
77
- export const buildRender =
78
- ( buildRenderComponent ) =>
79
- ( Component , options = { } , renderOptions = { } ) => {
80
- const baseElement =
81
- renderOptions . baseElement ?? options . target ?? document . body
82
-
83
- const target =
84
- options . target ?? baseElement . appendChild ( document . createElement ( 'div' ) )
85
-
86
- targetCache . add ( target )
51
+ render ( Component , { target, ...options } = { } , { container, queries } = { } ) {
52
+ container = container || document . body
53
+ target = target || container . appendChild ( document . createElement ( 'div' ) )
54
+ this . targetCache . add ( target )
87
55
88
56
const ComponentConstructor = Component . default || Component
89
57
90
- const renderComponent = buildRenderComponent ( {
91
- target,
92
- ComponentConstructor,
93
- } )
94
-
95
- let component = renderComponent ( { target, ...options } )
58
+ const component = this . renderComponent (
59
+ {
60
+ target,
61
+ ComponentConstructor,
62
+ } ,
63
+ options
64
+ )
96
65
97
66
return {
98
67
container,
@@ -109,35 +78,61 @@ export const buildRender =
109
78
await Svelte . tick ( )
110
79
} ,
111
80
unmount : ( ) => {
112
- cleanupComponent ( component )
81
+ this . cleanupComponent ( component )
113
82
} ,
114
83
...getQueriesForElement ( container , queries ) ,
115
84
}
116
85
}
117
86
118
- export const render = buildRender ( buildRenderComponent )
87
+ renderComponent ( { target, ComponentConstructor } , options ) {
88
+ options = { target, ...this . checkProps ( options ) }
89
+
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
+ }
104
+
105
+ return component
106
+ }
119
107
120
- export const cleanupComponent = ( component ) => {
121
- const inCache = componentCache . delete ( component )
108
+ cleanupComponent ( component ) {
109
+ const inCache = this . componentCache . delete ( component )
122
110
123
- if ( inCache ) {
124
- component . $destroy ( )
111
+ if ( inCache ) {
112
+ component . $destroy ( )
113
+ }
125
114
}
126
- }
127
115
128
- const cleanupTarget = ( target ) => {
129
- const inCache = targetCache . delete ( target )
116
+ cleanupTarget ( target ) {
117
+ const inCache = this . targetCache . delete ( target )
130
118
131
- if ( inCache && target . parentNode === document . body ) {
132
- document . body . removeChild ( target )
119
+ if ( inCache && target . parentNode === document . body ) {
120
+ document . body . removeChild ( target )
121
+ }
133
122
}
134
- }
135
123
136
- export const cleanup = ( ) => {
137
- componentCache . forEach ( cleanupComponent )
138
- targetCache . forEach ( cleanupTarget )
124
+ cleanup ( ) {
125
+ this . componentCache . forEach ( this . cleanupComponent . bind ( this ) )
126
+ this . targetCache . forEach ( this . cleanupTarget . bind ( this ) )
127
+ }
139
128
}
140
129
130
+ const instance = new SvelteTestingLibrary ( )
131
+
132
+ export const render = instance . render . bind ( instance )
133
+
134
+ export const cleanup = instance . cleanup . bind ( instance )
135
+
141
136
export const act = async ( fn ) => {
142
137
if ( fn ) {
143
138
await fn ( )
0 commit comments