Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte committed Oct 22, 2023
1 parent c4c3038 commit a8aada1
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 12 deletions.
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@
"version": "0.0.0",
"scripts": {
"dev": "vite dev",
"build": "vite build && npm run package",
"build": "vite build && pnpm run package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "npm run package",
"test": "npm run test:integration && npm run test:unit",
"prepublishOnly": "pnpm run package",
"test": "vitest",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write .",
"test:integration": "playwright test",
"test:unit": "vitest",
"release": "changeset publish",
"changeset": "changeset"
},
Expand Down Expand Up @@ -61,6 +59,7 @@
"types": "./dist/index.d.ts",
"type": "module",
"dependencies": {
"matchers": "link:@testing-library/jest-dom/matchers",
"svelte-persisted-store": "^0.7.0"
}
}
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions scripts/setupTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// setupTest.ts
/* eslint-disable @typescript-eslint/no-empty-function */
import { vi } from 'vitest';
import type { Navigation, Page } from '@sveltejs/kit';
import { readable } from 'svelte/store';
import * as environment from '$app/environment';
import * as navigation from '$app/navigation';
import * as stores from '$app/stores';
import { configure } from '@testing-library/dom';

configure({
asyncUtilTimeout: 1500
});

// Mock SvelteKit runtime module $app/environment
vi.mock('$app/environment', (): typeof environment => ({
browser: false,
dev: true,
building: false,
version: 'any'
}));

// Mock SvelteKit runtime module $app/navigation
vi.mock('$app/navigation', (): typeof navigation => ({
afterNavigate: () => {},
beforeNavigate: () => {},
disableScrollHandling: () => {},
goto: () => Promise.resolve(),
invalidate: () => Promise.resolve(),
invalidateAll: () => Promise.resolve(),
preloadData: () => Promise.resolve(),
preloadCode: () => Promise.resolve()
}));

// Mock SvelteKit runtime module $app/stores
vi.mock('$app/stores', (): typeof stores => {
const getStores: typeof stores.getStores = () => {
const navigating = readable<Navigation | null>(null);
const page = readable<Page>({
url: new URL('http://localhost'),
params: {},
route: {
id: null
},
status: 200,
error: null,
data: {},
form: undefined
});
const updated = { subscribe: readable(false).subscribe, check: async () => false };

return { navigating, page, updated };
};

const page: typeof stores.page = {
subscribe(fn) {
return getStores().page.subscribe(fn);
}
};
const navigating: typeof stores.navigating = {
subscribe(fn) {
return getStores().navigating.subscribe(fn);
}
};
const updated: typeof stores.updated = {
subscribe(fn) {
return getStores().updated.subscribe(fn);
},
check: async () => false
};

return {
getStores,
navigating,
page,
updated
};
});

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn()
}))
});
9 changes: 9 additions & 0 deletions src/tests/Mode.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script lang="ts">
import { ModeWatcher, toggleMode, setMode, mode } from '$lib';
</script>

<ModeWatcher />
<span data-testid="mode">{$mode}</span>
<button on:click={toggleMode} data-testid="toggle"> toggle </button>
<button on:click={() => setMode('light')} data-testid="light">light</button>
<button on:click={() => setMode('dark')} data-testid="dark">dark</button>
70 changes: 70 additions & 0 deletions src/tests/mode.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { render } from '@testing-library/svelte';
import { expect, it } from 'vitest';
import Mode from './Mode.svelte';
import userEvent from '@testing-library/user-event';

it('renders mode', async () => {
const { container } = render(Mode);
const rootEl = container.parentElement;
const classes = getClasses(rootEl);
expect(classes).toContain('dark');
});

it('toggles the mode', async () => {
const { container, getByTestId } = render(Mode);
const rootEl = container.parentElement;
const classes = getClasses(rootEl);
expect(classes).toContain('dark');
const toggle = getByTestId('toggle');
await userEvent.click(toggle);
const classes2 = getClasses(rootEl);
expect(classes2).not.toContain('dark');
await userEvent.click(toggle);
const classes3 = getClasses(rootEl);
expect(classes3).toContain('dark');
});

it('allows the user to set the mode', async () => {
const { container, getByTestId } = render(Mode);
const rootEl = container.parentElement;
const classes = getClasses(rootEl);
expect(classes).toContain('dark');
const light = getByTestId('light');
await userEvent.click(light);
const classes2 = getClasses(rootEl);
expect(classes2).not.toContain('dark');

const dark = getByTestId('dark');
await userEvent.click(dark);
const classes3 = getClasses(rootEl);
expect(classes3).toContain('dark');
});

it('keeps the mode store in sync with current mode', async () => {
const { container, getByTestId } = render(Mode);
const rootEl = container.parentElement;
const light = getByTestId('light');
const dark = getByTestId('dark');
const mode = getByTestId('mode');
const classes = getClasses(rootEl);
expect(classes).toContain('dark');
expect(mode.textContent).toBe('dark');

await userEvent.click(light);
const classes2 = getClasses(rootEl);
expect(classes2).not.toContain('dark');
expect(mode.textContent).toBe('light');

await userEvent.click(dark);
const classes3 = getClasses(rootEl);
expect(classes3).toContain('dark');
expect(mode.textContent).toBe('dark');
});

function getClasses(element: HTMLElement | null): string[] {
if (element === null) {
return [];
}
const classes = element.className.split(' ').filter((c) => c.length > 0);
return classes;
}
6 changes: 0 additions & 6 deletions tests/test.ts

This file was deleted.

14 changes: 13 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
include: ['src/**/*.{test,spec}.{js,ts}'],
// jest like globals
globals: true,
environment: 'jsdom',
// in-source testing
includeSource: ['src/**/*.{js,ts,svelte}'],
// Add @testing-library/jest-dom matchers & mocks of SvelteKit modules
setupFiles: ['./scripts/setupTest.ts'],
// Exclude files in v8
coverage: {
exclude: ['setupTest.ts']
},
alias: [{ find: /^svelte$/, replacement: 'svelte/internal' }]
}
});

0 comments on commit a8aada1

Please sign in to comment.