@testing-library/svelte-core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,19 +68,20 @@ const { baseElement, container, component, rerender, unmount } = render(
68
68
  )
69
69
  ```
70
70
 
71
- | Argument | Type | Description |
72
- | ------------------ | ------------------------------------------------------- | --------------------------------------------- |
73
- | `Component` | [Svelte component][svelte-component-docs] | An imported Svelte component |
74
- | `componentOptions` | `Props` or partial [`mount` options][svelte-mount-docs] | Options for how the component will be mounted |
75
- | `setupOptions` | `{ baseElement?: HTMLElement }` | Optionally override `baseElement` |
76
-
77
- | Result | Type | Description | Default |
78
- | ------------- | ------------------------------------------ | ---------------------------------------- | ----------------------------------- |
79
- | `baseElement` | `HTMLElement` | The base element | `document.body` |
80
- | `container` | `HTMLElement` | The component's immediate parent element | `<div>` appended to `document.body` |
81
- | `component` | [component exports][svelte-mount-docs] | The component's exports from `mount` | N/A |
82
- | `rerender` | `(props: Partial<Props>) => Promise<void>` | Update the component's props | N/A |
83
- | `unmount` | `() => void` | Unmount the component from the document | N/A |
71
+ | Argument | Type | Description |
72
+ | ------------------ | ------------------------------------------------------- | ------------------------------------------------------- |
73
+ | `Component` | [Svelte component][svelte-component-docs] | An imported Svelte component |
74
+ | `componentOptions` | `Props` or partial [`mount` options][svelte-mount-docs] | Options for how the component will be mounted |
75
+ | `setupOptions` | [`SetupOptions`](#setup) | Optionally override `baseElement` or wrap the component |
76
+
77
+ | Result | Type | Description | Default |
78
+ | ------------- | ------------------------------------------ | -------------------------------------------------- | ----------------------------------- |
79
+ | `baseElement` | `HTMLElement` | The base element | `document.body` |
80
+ | `container` | `HTMLElement` | The component's immediate parent element | `<div>` appended to `document.body` |
81
+ | `component` | [component exports][svelte-mount-docs] | The component's exports from `mount` | N/A |
82
+ | `wrapper` | [component exports][svelte-mount-docs] | The wrapper's exports, if a `wrapper` was provided | `undefined` |
83
+ | `rerender` | `(props: Partial<Props>) => Promise<void>` | Update the component's props | N/A |
84
+ | `unmount` | `() => void` | Unmount the component from the document | N/A |
84
85
 
85
86
  > \[!TIP]
86
87
  > Calling `render` is equivalent to calling [`setup`](#setup) followed by [`mount`](#mount)
@@ -90,7 +91,11 @@ const { baseElement, container, component, rerender, unmount } = render(
90
91
  > componentOptions,
91
92
  > setupOptions
92
93
  > )
93
- > const { component, rerender, unmount } = mount(Component, mountOptions)
94
+ > const { component, rerender, unmount } = mount(
95
+ > Component,
96
+ > mountOptions,
97
+ > setupOptions
98
+ > )
94
99
  > ```
95
100
 
96
101
  [svelte-component-docs]: https://svelte.dev/docs/svelte-components
@@ -110,7 +115,15 @@ const { baseElement, container, mountOptions } = setup(
110
115
  | Argument | Type | Description |
111
116
  | ------------------ | ------------------------------------------------------- | --------------------------------------------- |
112
117
  | `componentOptions` | `Props` or partial [`mount` options][svelte-mount-docs] | Options for how the component will be mounted |
113
- | `setupOptions` | `{ baseElement?: HTMLElement }` | Optionally override `baseElement` |
118
+ | `setupOptions` | `SetupOptions` | Configure the document and wrap the component |
119
+
120
+ `setupOptions` accepts the following fields, all optional:
121
+
122
+ | Field | Type | Description | Default |
123
+ | -------------- | ----------------------------------------- | --------------------------------------------------------------------- | --------------- |
124
+ | `baseElement` | `HTMLElement` | The base element to bind queries to | `document.body` |
125
+ | `wrapper` | [Svelte component][svelte-component-docs] | A component to wrap the component under test, e.g. a context provider | `undefined` |
126
+ | `wrapperProps` | `Props` | Props to pass to the `wrapper` component | `undefined` |
114
127
 
115
128
  | Result | Type | Description | Default |
116
129
  | -------------- | ------------------------------------ | ---------------------------------------- | ----------------------------------- |
@@ -123,19 +136,25 @@ const { baseElement, container, mountOptions } = setup(
123
136
  Mount a Svelte component into the document.
124
137
 
125
138
  ```ts
126
- const { component, unmount, rerender } = mount(Component, mountOptions)
139
+ const { component, wrapper, unmount, rerender } = mount(
140
+ Component,
141
+ mountOptions,
142
+ setupOptions
143
+ )
127
144
  ```
128
145
 
129
- | Argument | Type | Description |
130
- | -------------- | ----------------------------------------- | -------------------------------------------- |
131
- | `Component` | [Svelte component][svelte-component-docs] | An imported Svelte component |
132
- | `mountOptions` | [component options][svelte-mount-docs] | Options to pass to Svelte's `mount` function |
133
-
134
- | Result | Type | Description |
135
- | ----------- | ------------------------------------------ | --------------------------------------- |
136
- | `component` | [component exports][svelte-mount-docs] | The component's exports from `mount` |
137
- | `unmount` | `() => void` | Unmount the component from the document |
138
- | `rerender` | `(props: Partial<Props>) => Promise<void>` | Update the component's props |
146
+ | Argument | Type | Description |
147
+ | -------------- | ----------------------------------------- | --------------------------------------------------------- |
148
+ | `Component` | [Svelte component][svelte-component-docs] | An imported Svelte component |
149
+ | `mountOptions` | [component options][svelte-mount-docs] | Options to pass to Svelte's `mount` function |
150
+ | `setupOptions` | [`SetupOptions`](#setup) | Optionally wrap the component (`wrapper`, `wrapperProps`) |
151
+
152
+ | Result | Type | Description |
153
+ | ----------- | ------------------------------------------ | -------------------------------------------------- |
154
+ | `component` | [component exports][svelte-mount-docs] | The component's exports from `mount` |
155
+ | `wrapper` | [component exports][svelte-mount-docs] | The wrapper's exports, if a `wrapper` was provided |
156
+ | `unmount` | `() => void` | Unmount the component from the document |
157
+ | `rerender` | `(props: Partial<Props>) => Promise<void>` | Update the component's props |
139
158
 
140
159
  ### `cleanup`
141
160
 
package/dist/mount.d.ts CHANGED
@@ -2,9 +2,12 @@
2
2
  * Render a Svelte component into the document.
3
3
  *
4
4
  * @template {import('../types.js').Component} C
5
+ * @template {import('../types.js').Component} [W=never]
6
+ *
5
7
  * @param {import('../types.js').ComponentImport<C>} Component
6
8
  * @param {import('../types.js').MountOptions<C>} options
9
+ * @param {import('../types.js').SetupOptions<W>} [setupOptions]
7
10
  * @returns {import('../types.js').MountResult<C>}
8
11
  */
9
- export function mount<C extends import("../types.js").Component>(Component: import("../types.js").ComponentImport<C>, options: import("../types.js").MountOptions<C>): import("../types.js").MountResult<C>;
12
+ export function mount<C extends import("../types.js").Component, W extends import("../types.js").Component = never>(Component: import("../types.js").ComponentImport<C>, options: import("../types.js").MountOptions<C>, setupOptions?: import("../types.js").SetupOptions<W>): import("../types.js").MountResult<C>;
10
13
  //# sourceMappingURL=mount.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.js"],"names":[],"mappings":"AA2EA;;;;;;;GAOG;AACH,sBAL+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,aACnC,OAAO,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,WACxC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,GACnC,OAAO,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAwBhD"}
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.js"],"names":[],"mappings":"AA8HA;;;;;;;;;;GAUG;AACH,sBAR+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,EACE,CAAC,SAApC,OAAQ,aAAa,EAAE,SAAU,qBAEnC,OAAO,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,WACxC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,iBACrC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,GACnC,OAAO,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAqChD"}
package/dist/render.d.ts CHANGED
@@ -2,11 +2,12 @@
2
2
  * Render a component into the document.
3
3
  *
4
4
  * @template {import('../types.js').Component} C
5
+ * @template {import('../types.js').Component} [W=never]
5
6
  *
6
7
  * @param {import('../types.js').ComponentImport<C>} Component - The component to render.
7
8
  * @param {import('../types.js').ComponentOptions<C>} componentOptions - Customize how Svelte renders the component.
8
- * @param {import('../types.js').SetupOptions} setupOptions - Customize how the document is set up.
9
- * @returns {import('../types.js').RenderResult<C>} The rendered component.
9
+ * @param {import('../types.js').SetupOptions<W>} setupOptions - Customize how the document and component is set up.
10
+ * @returns {import('../types.js').RenderResult<C, W>} The rendered component.
10
11
  */
11
- export function render<C extends import("../types.js").Component>(Component: import("../types.js").ComponentImport<C>, componentOptions: import("../types.js").ComponentOptions<C>, setupOptions?: import("../types.js").SetupOptions): import("../types.js").RenderResult<C>;
12
+ export function render<C extends import("../types.js").Component, W extends import("../types.js").Component = never>(Component: import("../types.js").ComponentImport<C>, componentOptions: import("../types.js").ComponentOptions<C>, setupOptions?: import("../types.js").SetupOptions<W>): import("../types.js").RenderResult<C, W>;
12
13
  //# sourceMappingURL=render.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.js"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,uBAP+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,aAEnC,OAAO,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,oBACxC,OAAO,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC,iBACzC,OAAO,aAAa,EAAE,YAAY,GAChC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,CAOjD"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.js"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,uBAR+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,EACE,CAAC,SAApC,OAAQ,aAAa,EAAE,SAAU,qBAEnC,OAAO,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC,oBACxC,OAAO,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC,iBACzC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,GACnC,OAAO,aAAa,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAOpD"}
package/dist/setup.d.ts CHANGED
@@ -2,11 +2,12 @@
2
2
  * Set up the document to render a component.
3
3
  *
4
4
  * @template {import('../types.js').Component} C
5
+ *
5
6
  * @param {import('../types.js').ComponentOptions<C>} componentOptions - props or mount options
6
- * @param {import('../types.js').SetupOptions} setupOptions - base element of the document to bind any queries
7
+ * @param {import('../types.js').SetupOptions<any>} setupOptions - base element of the document to bind any queries
7
8
  * @returns {import('../types.js').SetupResult<C>}
8
9
  */
9
- export function setup<C extends import("../types.js").Component>(componentOptions: import("../types.js").ComponentOptions<C>, setupOptions?: import("../types.js").SetupOptions): import("../types.js").SetupResult<C>;
10
+ export function setup<C extends import("../types.js").Component>(componentOptions: import("../types.js").ComponentOptions<C>, setupOptions?: import("../types.js").SetupOptions<any>): import("../types.js").SetupResult<C>;
10
11
  export class UnknownSvelteOptionsError extends TypeError {
11
12
  constructor(unknownOptions: any);
12
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.js"],"names":[],"mappings":"AAsDA;;;;;;;GAOG;AACH,sBAL+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,oBACnC,OAAO,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC,iBACzC,OAAO,aAAa,EAAE,YAAY,GAChC,OAAO,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAuBhD;AA1ED;IACE,iCAaC;CACF"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.js"],"names":[],"mappings":"AAsDA;;;;;;;;GAQG;AACH,sBAN+C,CAAC,SAAnC,OAAQ,aAAa,EAAE,SAAU,oBAEnC,OAAO,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC,iBACzC,OAAO,aAAa,EAAE,YAAY,CAAC,GAAG,CAAC,GACrC,OAAO,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC,CAuBhD;AA3ED;IACE,iCAaC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testing-library/svelte-core",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Core rendering and cleanup logic for Svelte testing utilities.",
5
5
  "exports": {
6
6
  ".": {
package/src/mount.js CHANGED
@@ -6,6 +6,7 @@ import * as Svelte from 'svelte'
6
6
  import { addCleanupTask, removeCleanupTask } from './cleanup.js'
7
7
  import { createProps } from './props.svelte.js'
8
8
  import { IS_MODERN_SVELTE } from './svelte-version.js'
9
+ import WrapperScaffold from './wrapper-scaffold.svelte'
9
10
 
10
11
  /**
11
12
  * Mount a modern Svelte 5 component into the DOM.
@@ -73,22 +74,82 @@ const mountLegacy = (Component, options) => {
73
74
  /** The mount method in use. */
74
75
  const mountComponent = IS_MODERN_SVELTE ? mountModern : mountLegacy
75
76
 
77
+ /**
78
+ * Extract a component from an import.
79
+ *
80
+ * Allows a dynamic `import('component.svelte')` to be used
81
+ * for component values.
82
+ *
83
+ * @template {import('../types.js').Component} C
84
+ * @param {import('../types.js').ComponentImport<C>} componentImport
85
+ * @returns {import('../types.js').ComponentType<C>}
86
+ */
87
+ const unwrapComponentImport = (componentImport) => {
88
+ return 'default' in componentImport
89
+ ? componentImport.default
90
+ : componentImport
91
+ }
92
+
76
93
  /**
77
94
  * Render a Svelte component into the document.
78
95
  *
79
96
  * @template {import('../types.js').Component} C
97
+ * @template {import('../types.js').Component} [W=never]
98
+ *
99
+ * @param {import('../types.js').ComponentImport<C>} Component
100
+ * @param {import('../types.js').MountOptions<C>} mountOptions
101
+ * @param {import('../types.js').SetupOptions<W>} [setupOptions]
102
+ * @returns {{componentToMount: import('../types.js').ComponentType<C | W>, mountOptions: import('../types.js').MountOptions<C | W>, isWrapper: boolean}}
103
+ */
104
+ const setupComponent = (Component, mountOptions, setupOptions = {}) => {
105
+ const componentToMount = unwrapComponentImport(Component)
106
+ const { wrapper, wrapperProps } = setupOptions
107
+
108
+ if (wrapper) {
109
+ return {
110
+ isWrapper: true,
111
+ componentToMount: WrapperScaffold,
112
+ mountOptions: {
113
+ ...mountOptions,
114
+ props: {
115
+ wrapper: unwrapComponentImport(wrapper),
116
+ wrapperProps,
117
+ component: componentToMount,
118
+ componentProps: mountOptions.props,
119
+ },
120
+ },
121
+ }
122
+ }
123
+
124
+ return { isWrapper: false, componentToMount, mountOptions }
125
+ }
126
+
127
+ /**
128
+ * Render a Svelte component into the document.
129
+ *
130
+ * @template {import('../types.js').Component} C
131
+ * @template {import('../types.js').Component} [W=never]
132
+ *
80
133
  * @param {import('../types.js').ComponentImport<C>} Component
81
134
  * @param {import('../types.js').MountOptions<C>} options
135
+ * @param {import('../types.js').SetupOptions<W>} [setupOptions]
82
136
  * @returns {import('../types.js').MountResult<C>}
83
137
  */
84
- const mount = (Component, options) => {
138
+ const mount = (Component, options, setupOptions = {}) => {
139
+ const { componentToMount, mountOptions, isWrapper } = setupComponent(
140
+ Component,
141
+ options,
142
+ setupOptions
143
+ )
144
+
85
145
  const { component, unmount, rerender } = mountComponent(
86
- 'default' in Component ? Component.default : Component,
87
- options
146
+ componentToMount,
147
+ mountOptions
88
148
  )
89
149
 
90
150
  return {
91
- component,
151
+ component: isWrapper ? component.getComponent() : component,
152
+ wrapper: isWrapper ? component.getWrapper() : undefined,
92
153
  unmount,
93
154
  rerender: async (props) => {
94
155
  if ('props' in props) {
@@ -98,6 +159,12 @@ const mount = (Component, options) => {
98
159
  props = props.props
99
160
  }
100
161
 
162
+ if (isWrapper) {
163
+ props = {
164
+ componentProps: { ...component.getComponentProps(), ...props },
165
+ }
166
+ }
167
+
101
168
  rerender(props)
102
169
  // Await the next tick for Svelte 3/4, which cannot flush changes synchronously
103
170
  await Svelte.tick()
package/src/render.js CHANGED
@@ -5,15 +5,16 @@ import { setup } from './setup.js'
5
5
  * Render a component into the document.
6
6
  *
7
7
  * @template {import('../types.js').Component} C
8
+ * @template {import('../types.js').Component} [W=never]
8
9
  *
9
10
  * @param {import('../types.js').ComponentImport<C>} Component - The component to render.
10
11
  * @param {import('../types.js').ComponentOptions<C>} componentOptions - Customize how Svelte renders the component.
11
- * @param {import('../types.js').SetupOptions} setupOptions - Customize how the document is set up.
12
- * @returns {import('../types.js').RenderResult<C>} The rendered component.
12
+ * @param {import('../types.js').SetupOptions<W>} setupOptions - Customize how the document and component is set up.
13
+ * @returns {import('../types.js').RenderResult<C, W>} The rendered component.
13
14
  */
14
15
  const render = (Component, componentOptions, setupOptions = {}) => {
15
16
  const { mountOptions, ...setupResult } = setup(componentOptions, setupOptions)
16
- const mountResult = mount(Component, mountOptions)
17
+ const mountResult = mount(Component, mountOptions, setupOptions)
17
18
 
18
19
  return { ...setupResult, ...mountResult }
19
20
  }
package/src/setup.js CHANGED
@@ -56,8 +56,9 @@ const validateOptions = (options) => {
56
56
  * Set up the document to render a component.
57
57
  *
58
58
  * @template {import('../types.js').Component} C
59
+ *
59
60
  * @param {import('../types.js').ComponentOptions<C>} componentOptions - props or mount options
60
- * @param {import('../types.js').SetupOptions} setupOptions - base element of the document to bind any queries
61
+ * @param {import('../types.js').SetupOptions<any>} setupOptions - base element of the document to bind any queries
61
62
  * @returns {import('../types.js').SetupResult<C>}
62
63
  */
63
64
  const setup = (componentOptions, setupOptions = {}) => {
@@ -0,0 +1,21 @@
1
+ <script>
2
+ export let wrapper
3
+ export let wrapperProps
4
+ export let component
5
+ export let componentProps
6
+
7
+ let wrapperInstance
8
+ let componentInstance
9
+
10
+ export const getWrapper = () => wrapperInstance
11
+ export const getComponent = () => componentInstance
12
+ export const getComponentProps = () => componentProps
13
+ </script>
14
+
15
+ <svelte:component this={wrapper} bind:this={wrapperInstance} {...wrapperProps}>
16
+ <svelte:component
17
+ this={component}
18
+ bind:this={componentInstance}
19
+ {...componentProps}
20
+ />
21
+ </svelte:component>
package/types.d.ts CHANGED
@@ -91,19 +91,25 @@ export type Rerender<C extends Component> = (
91
91
  ) => Promise<void>
92
92
 
93
93
  /** The result of mounting a component into the document. */
94
- export interface MountResult<C extends Component> {
94
+ export interface MountResult<C extends Component, W extends Component = never> {
95
95
  /** The mounted component's exports. */
96
96
  component: Exports<C>
97
+ /** The mounted wrapper's exports, if a wrapper was provided. */
98
+ wrapper: Exports<W>
97
99
  /** Unmount the component. */
98
100
  unmount: () => void
99
101
  /** Rerender the component. */
100
102
  rerender: Rerender<C>
101
103
  }
102
104
 
103
- /** Options for configuring the document. */
104
- export interface SetupOptions {
105
+ /** Options for configuring the component and document. */
106
+ export interface SetupOptions<W extends Component = never> {
105
107
  /** The base document element, `document.body` if unspecified. */
106
108
  baseElement?: HTMLElement
109
+ /** A wrapper component. */
110
+ wrapper?: ComponentImport<W>
111
+ /** Wrapper component props. */
112
+ wrapperProps?: Props<W>
107
113
  }
108
114
 
109
115
  /** The result of setting up the document for rendering. */
@@ -117,13 +123,18 @@ export interface SetupResult<C extends Component> {
117
123
  }
118
124
 
119
125
  /** The result of setting up the document and rendering the component. */
120
- export interface RenderResult<C extends Component> {
126
+ export interface RenderResult<
127
+ C extends Component,
128
+ W extends Component = never,
129
+ > {
121
130
  /** The base document element, usually `document.body`. */
122
131
  baseElement: HTMLElement
123
132
  /** The component's immediate container element, usually a `<div>` appended to `document.body`. */
124
133
  container: HTMLElement
125
134
  /** The mounted component's exports. */
126
135
  component: Exports<C>
136
+ /** The mounted wrapper's exports, if a wrapper was provided. */
137
+ wrapper: Exports<W>
127
138
  /** Unmount the component. */
128
139
  unmount: () => void
129
140
  /** Rerender the component. */