svelte-component-double Svelte Themes

Svelte Component Double

A simple test double for Svelte 3 components

npm version install size build status license

svelte-component-double

An test double for Svelte components. It works with Vitest, Jest and Jasmine.

Installation

First install the package:

npm install --save-dev svelte-component-double

Vitest setup & usage

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.

Jasmine setup & usage

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();
  });
});

Matchers

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.

Identifying stubbed DOM elements

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.

Triggering two-way bindings

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.

Component property reference

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.

Contributing

All contributions are welcome. Please feel free to create issues or raise PRs.

Top categories

Loading Svelte Themes