Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing svelte components with svelte/store

When testing svelte components, using jest & @testing-library/svelte, the state is shared between tests, is there away to tear down after each test so i have more isolated unit tests.

store/theme

import { writable } from "svelte/store";

export const LOCAL_STORAGE_KEY = "current:theme";

export const THEMES = {
  DARK: "dark",
  LIGHT: "light"
};

export const MATCH_DARK_THEME = "(prefers-color-scheme: dark)";

export const IS_USER_PREFERNCE_DARK =
  window.matchMedia && window.matchMedia(MATCH_DARK_THEME).matches;

export const DEFAULT_THEME =
  localStorage.getItem(LOCAL_STORAGE_KEY) || IS_USER_PREFERNCE_DARK
    ? THEMES.DARK
    : THEMES.LIGHT;

export const theme = writable(DEFAULT_THEME);

because there is no DI the store is shared between tests, I could reset the value to default in the beforeEach but trying to see if there is a better solution.

ThemeSwitcher.spec.js

it("should be change body class on click", async () => {
  const { container } = render(ThemeSwitcher);

  expect(container.className).toEqual("theme-light");

  await fireEvent.click(getButton(container));

  expect(container.className).toEqual("theme-dark");
});

it("should render the sun if in light mode", async () => {
  const { getByText } = render(ThemeSwitcher);
  //default should be light mode but returns dark.
  const sun = getByText("Light theme on: Sun");

  expect(sun).toBeTruthy();
});
like image 240
Joe Warner Avatar asked Apr 01 '20 12:04

Joe Warner


People also ask

Does jest work with svelte?

Jest SetupTo compile Svelte components, it's also needed to install svelte-jester. After the installation is complete, Jest and Babel have to be configured. To configure Jest, create a jest. config.

How does svelte store work?

Svelte stores offer similar features for state management. A store is an object with a subscribe() method that allows interested parties to be notified whenever the store value changes, and an optional set() method that allows you to set new values for the store. This minimal API is known as the store contract.

How do I update my svelte store?

Click the stores. js tab to see the definition of count . It's a writable store, which means it has set and update methods in addition to subscribe . Clicking the + button should now update the count.

What are svelte stores and how do they work?

Congratulations - you’ve built your first app with Svelte stores. Svelte stores give you an opportunity to share and manipulate data between components without having to pass that data up and down the component hierarchy. Centrally located global stores can reduce the complexity of your app and reduce the likelihood of introducing bugs.

How to use the svelte testing library?

To use the Svelte Testing Library, the @testing-library/svelte package has to be installed. After all the setup, we can start writing tests. The test will look familiar to you if you've already used a Testing Library. Everything is set, and now we can finally run the tests.

Can I use $store outside of svelte components?

Note: outside of Svelte components you cannot use the $store syntax. That's because the Svelte compiler won't touch anything outside of Svelte components. In that case you'll have to rely on the store.subscribe () and store.set () methods. Improving our Alert component

How do I use update () method in svelte store?

Inside this function, the code is calling an update () method on both itemsInCart and cartContents. update () is a built-in method on Svelte stores that you can use to update the value of a given writable store. update () takes one argument: a callback function.


Video Answer


1 Answers

I prefer to wrap svelte store in generic class for easy to use.

this is my Store.ts

import { writable, get, Writable } from "svelte/store"

/** Callback to inform of a value updates. */
export declare type Subscriber<T> = (value: T) => void
/** Unsubscribes from value updates. */
export declare type Unsubscriber = () => void
/** Callback to update a value. */
export declare type Updater<T> = (value: T) => T
/** Cleanup logic callback. */
export declare type Invalidator<T> = (value?: T) => void

class Store<T> implements Writable<T> {
    private intialValue: T
    private wrappee: Writable<T>

    // implements Writable
    subscribe: (run: Subscriber<T>, invalidate?: Invalidator<T>) => Unsubscriber
    set: (value: T) => void

    update: (updater: Updater<T>) => void

    constructor(value: T) {
        this.intialValue = value
        const _store = writable(value)
        const { subscribe, set, update } = _store
        this.subscribe = subscribe
        this.set = set
        this.update = update
        this.wrappee = _store
    }

    get() {
        return get(this.wrappee)
    }

    reset() {
        this.set(this.intialValue)
    }

    refresh() {
        this.set(this.get())
    }
}

You can extend generic Store class to create your new store like that.

arrayStringStore.ts

export default class ArrayStringStore extends Store<string[]> {
    constructor(arr: string[] = []) {
        super(arr)
    }

    // easy to add more convenience method
    add(item: string) {
        this.update(arr => [...arr, item])
    }
}

For example: I have a instance of ArrayStringStore that is exampleStore

const exampleStore = new ArrayStringStore()

You can easy reset value of that store before each test case with

in your test file.

beforeEach(() => {
    exampleStore.reset()
})

Note: you can get value of store with exampleStore.get(), do not need to import { get } from svelte/store in every file.

like image 133
Long Vu Avatar answered Oct 20 '22 08:10

Long Vu