A test double for Svelte 3 components
npm install svelte-component-doubleAn test double for Svelte components. It works with Vitest, Jest and Jasmine.
First install the package:
``bash`
npm install --save-dev svelte-component-double
Create a file in your Vite project, named test/registerSvelteComponentDouble.js, with the followig content:
`javascript
import { expect } from 'vitest';
import * as matchers from 'svelte-component-double/vitest';
expect.extend(matchers);
import { componentDouble } from 'svelte-component-double';
globalThis.componentDouble = componentDouble;
`
Then update your vi.config.js file to include this as a setup file for Vitest:
`javascript`
const config = {
...,
test: {
...,
setupFiles: [
...,
'./test/registerSvelteComponentDouble.js'
]
}
}
Then you can begin to write mocked tests like this:
`javascript
import {
describe,
it,
expect,
beforeEach,
vi
} from 'vitest';
import {
render,
screen
} from '@testing-library/svelte';
import Child from './Child.svelte';
import Parent from './Parent.svelte';
vi.mock('./Child.svelte', async () => ({
default: componentDouble('Child')
}));
describe('Parent component', () => {
beforeEach(Child.reset);
it('renders a Child with the right props', () => {
render(Parent);
expect(Child).toBeRenderedWithProps({
foo: "bar"
});
});
});
`
Since mocks are active for the entire module, you may wish to place mocked tests in a separate test file e.g. parent.mocks.test.js, keeping parent.test.js free for non-mocked tests.
Add the following helper in spec/support/jasmine.json.
`json`
"helpers": [
"../node_modules/svelte-component-double/lib/matchers/jasmine.js"
]
You'll need to use a mocking tool like babel-plugin-rewire-exports.
In the example below, Parent is the component under test, and Child is being spied on.
`javascript
import Child, { rewire as rewire$Child, restore } from '../src/Child.svelte';
import Parent from '../src/Parent.svelte';
import { componentDouble } from 'svelte-component-double';
describe('Parent component', () => {
it('renders a Child', () => {
// ensure JSDOM is set up and ready to go
rewire$Child(componentDouble(Child));
const el = document.createElement('div');
new Parent({ target: el });
expect(Child).toHaveBeenCalled();
expect(el.querySelector(spySelector(Child))).not.toBeNull();
});
});
`
The expect(component) function has the following matchers available.
| Matcher | Description |
| -------- | ----------- |
| toBeRendered() | Passes if there was at least one instance of the component instantiated in the current document. |toBeRenderedIn(container)
| | Same but with a specific DOM container instead of | document.body. |toBeRenderedWithProps(props)
| | Passes if there was at least one instance of the component instantiated with these exact props. |toBeRenderedWithPropsIn(props, container)
| | Same as above but with a specic DOM container instead of document.body. |
A spied/stubbed component will be rendered into the DOM like this:
`html`
You can use the selector function to return a selector that will match all instances of a rendered double. So for the example above, calling spySelector(Child) will return "div[class=spy-Child]".
You can use the instanceSelector(n) to return a selector that matches a specific instance of the component.
If your stubbed component uses a two-way binding you can trigger that binding to update using the updateBoundValue function.
For example, say you have the component TagList which can be used like this:
`html`
Then you can test how your component responds to updates to tags like this:
`javascript
rewire$TagList(componentDouble(TagList));
mount(
TagList.firstInstance().updateBoundValue(
component, "tags", ["a", "b", "c"]);
`
Warning: updateBoundValue has been tested with Svelte version 3.16. It is incompatible with 3.15 and below.
All of these functions are available on your component double type.
| Property/function | Type | Description |
| ----------------- | ---- | ----------- |
| instances | Array of instances | Each instance of the component that has been rendered. |reset()
| | Function | Resets a mocked component between tests. Call this in your beforeEach block. |selector()
| | Function | Selector for _all_ instances of this double. |instanceSelector(n)
| | Function | Selector for a single instances of this double. |findMatching(matchFn)
| | Function | Find the call whose props match the matchFn predicate |firstInstance()
| | Function | Returns the first instance of a component, which you an then manipulate using functions such as updateBoundValue (see note above). |lastInstance()
| | Function | Returns the last instance of a component. |getInstanceFromElement(domElement)
| | Function | Returns the component instance that rendered the given DOM element. |mountedInstances()
| | Function | Returns only the instances that are currently rendered in the DOM. |propsOfAllInstances()
| | Function | Returns an array for props for each of the rendered instance of this component.|dispatch(event)
| | Function | Dispatches an event to the last rendered component. You can also call dispatch` on individual instances. |
All contributions are welcome. Please feel free to create issues or raise PRs.