An test double for Svelte components. It works with Vitest, Jest and Jasmine.
First install the package:
npm install --save-dev svelte-component-double
Create a file in your Vite project, named test/registerSvelteComponentDouble.js
, with the followig content:
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:
const config = {
...,
test: {
...,
setupFiles: [
...,
'./test/registerSvelteComponentDouble.js'
]
}
}
Then you can begin to write mocked tests like this:
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
.
"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.
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 |
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:
<div class="spy-ComponentName" id="spy-ComponentName-instanceNumber" />
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:
<TagList bind:tags={tags} />
Then you can test how your component responds to updates to tags
like this:
rewire$TagList(componentDouble(TagList));
mount(<your component that uses TagList>);
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.