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();
});
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.
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.
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.
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.
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.
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
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.
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 toimport { get } from svelte/store
in every file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With