Testing Svelte context with component hierarchies
Tags: javascript, webdev, tutorial, testing
Published: 2020-01-16 17:10:33

---
title: Testing Svelte context with component hierarchies
published: true
description: Unit testing on the front-end!
tags: javascript, webdev, tutorial, testing
series: Writing unit tests for Svelte
---

In [the previous part of this series](https://dev.to/d_ir/mocking-svelte-components-5597) I looked at how to use mocking effectively when testing parent-child component relationships.

But that isn’t the only way of dealing with parent-child components, and component hierarchies in general. In this part I’ll look at testing two components in the same test suite. So far I’ve found this useful when dealing the Svelte’s context API.

All of the code samples in the post are available in this demo repo:

{% github dirv/svelte-testing-demo no-readme %}

# An example

Suppose you create a `Menu` component and a `MenuItem` component. `Menu` is responsible for opening and closing a list of items, and `MenuItem` represents a single item in that list. Crucially, it’s the `MenuItem`’s responsibility to close the `Menu` when it is selected.

Here’s `Menu`. I’ve simplified this by removing styles and by including only functionality that’s relevant to this post.

```html
<script context="module">
  export const key = {};
</script>

<script>
  import { setContext } from 'svelte';

  let open = false;

  const toggleMenu = () => open = !open;

  setContext(key, {
    toggleMenu
  });
</script>

<button on:click={toggleMenu} class="icon">Menu</button>
{#if open}
<div on:click={toggleMenu}>
  <slot />
</div>
{/if}
```

And here’s `MenuItem` (again, this is a simplified implementation).

```html
<script>
  import { getContext, tick } from "svelte";
  import { key } from "./Menu.svelte";

  export let action;

  const { toggleMenu } = getContext(key);

  const closeMenuAndAct = async (event) => {
    event.stopPropagation();
    toggleMenu();
    await tick();
    action();
  };
</script>

<button on:click="{closeMenuAndAct}">
  <slot />
</button>
```

Both of these components are coupled in two ways.

First, `Menu` uses `<slot>` to display all its children and it’s expected that some of these children will be instances of `MenuItem`.

Second, both components use the context API to share the `toggleMenu` function. `MenuItem`s can communicate with the parent by invoking the `toggleMenu` function, which tells the `Menu` it’s time to close.

### Could we programmatically call the context API to test `Menu` and `MenuItem` independently?

As far as I can tell, no we can’t. In order to do these we’d need to manipulate the context API. For example, for the `MenuItem` we’d need to make available a spy `toggleMenu` function that we could then assert on to check it was invoked.

```javascript
it("invokes the toggleMenu context function", () => {
  // ? set up context here ?
});
```

Trouble is, there’s no supported way of calling the context API outside of components themselves. We could probably do it by using the `component.$$` property in the way we did with bound values in the last part, but that’s at risk of breaking in future.

Besides, these two components are meant to be used together, so why not test them together?

### This is one place where React has Svelte beat!

Because React allows inline JSX, we could simply write a test like this:

```javascript
const menuBox = () => container.querySelector(".overlay");

it("closes the menu when clicking the menuItem", () => {
  mount(<Menu><MenuItem /></Menu>);
  click(menuItem());
  expect(menuBox()).not.toBeNull();
});
```

Unfortunately Svelte components must be defined in their own files, so we can’t do little inline hierarchies like this.

### Solution: define a test component for each test

In the test repo I have a directory `spec/components` where I keep little hierachies of components for specific tests. Sometimes the same test component can be used for multiple tests.

Here’s `spec/components/IsolatedMenuItem.svelte`:

```html
<script>
  import Menu from "../../src/Menu.svelte";
</script>

<Menu>
  <img alt="menu" slot="icon" src="menu.png" />
</Menu>
```

There are a couple of tests I can write with this. First, the test that checks the menu is closed.

Here’s `spec/Menu.spec.js` with just the first test&mdash;notice that I named the file after the parent component, but it’s testing _both_ the parent and child.

```javascript
import { tick } from "svelte";
import { mount, asSvelteComponent } from "./support/svelte.js";
import Menu from "../src/Menu.svelte";
import IsolatedMenuItem from "./components/IsolatedMenuItem.svelte";

const menuIcon = () => container.querySelector(".icon");
const menuBox = () => container.querySelector("div[class*=overlay]");

const click = async formElement => {
  const evt = document.createEvent("MouseEvents");
  evt.initEvent("click", true, true);
  formElement.dispatchEvent(evt);
  await tick();
  return evt;
};

describe(Menu.name, () => {
  asSvelteComponent();

  it("closes the menu when a menu item is selected", async () => {
    mount(IsolatedMenuItem);
    await click(menuIcon());
    await click(menuBox().querySelector("button"));
    expect(menuBox()).toBe(null);
  });
});
```

Notice how similar this is to the React version above. The difference is just that the component exists within its own file instead of being written inline.

(By the way, I think this is the first time in the series that I’ve shown any DOM events... `click` is something that could be cleaned up a little. We’ll look at that in the next post!)

The second test uses the `spy` prop of `IsolatedMenuItem`.

```javascript
it("performs action when menu item chosen", async () => {
  const action = jasmine.createSpy();
  mount(IsolatedMenuItem, { spy: action });
  await click(menuIcon());
  await click(menuBox().querySelector("button"));
  expect(action).toHaveBeenCalled();
});
```

For this test component I named the prop `spy`, which is used to set the `action` prop on `MenuItem`. Perhaps I should have kept its name as `action`. The benefit of naming it `spy` is that it’s clear what its purpose is for. But I'm still undecided if that’s a benefit or not.

## Use with `svelte-routing`

I’ve also used this with [`svelte-routing`](https://github.com/EmilTholin/svelte-routing) when I defined my own wrapped version of `Route`. These classes also use the context API so it’s similar to the example shown above.

In the next (and final!) post in this series, we’ll look at raising events like the one we saw here, the `click` event, and how we can test components together with more complex browser APIs like `setTimeout`.