Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a60dbcfa51 | |||
| 8fc8a7ee6f | |||
| cbc978df6d |
@@ -43,6 +43,12 @@ jobs:
|
|||||||
- name: Type Check
|
- name: Type Check
|
||||||
run: yarn check
|
run: yarn check
|
||||||
|
|
||||||
|
- name: Run Unit Tests
|
||||||
|
run: yarn test:unit
|
||||||
|
|
||||||
|
- name: Run Component Tests
|
||||||
|
run: yarn test:component
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
needs: build # Only runs if tests/lint pass
|
needs: build # Only runs if tests/lint pass
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ pre-commit:
|
|||||||
pre-push:
|
pre-push:
|
||||||
parallel: true
|
parallel: true
|
||||||
commands:
|
commands:
|
||||||
|
test-unit:
|
||||||
|
run: yarn test:unit
|
||||||
|
test-component:
|
||||||
|
run: yarn test:component
|
||||||
type-check:
|
type-check:
|
||||||
run: yarn tsc --noEmit
|
run: yarn tsc --noEmit
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export {
|
|||||||
mapManagerToParams,
|
mapManagerToParams,
|
||||||
} from './lib';
|
} from './lib';
|
||||||
|
|
||||||
|
export { filtersStore } from './model/state/filters.svelte';
|
||||||
export { filterManager } from './model/state/manager.svelte';
|
export { filterManager } from './model/state/manager.svelte';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,29 +1,40 @@
|
|||||||
import { filterManager } from '$features/GetFonts';
|
import {
|
||||||
|
filterManager,
|
||||||
|
filtersStore,
|
||||||
|
} from '$features/GetFonts';
|
||||||
import {
|
import {
|
||||||
render,
|
render,
|
||||||
screen,
|
screen,
|
||||||
} from '@testing-library/svelte';
|
} from '@testing-library/svelte';
|
||||||
|
import { vi } from 'vitest';
|
||||||
import Filters from './Filters.svelte';
|
import Filters from './Filters.svelte';
|
||||||
|
|
||||||
describe('Filters', () => {
|
describe('Filters', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
// Clear groups and mock filtersStore to be empty so the auto-sync effect doesn't overwrite us
|
||||||
filterManager.setGroups([]);
|
filterManager.setGroups([]);
|
||||||
|
vi.spyOn(filtersStore, 'filters', 'get').mockReturnValue([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
it('renders nothing when filter groups are empty', () => {
|
it('renders nothing when filter groups are empty', () => {
|
||||||
const { container } = render(Filters);
|
const { container } = render(Filters);
|
||||||
expect(container.firstElementChild).toBeNull();
|
// It might render an empty container if the component has one, but we expect no children
|
||||||
|
expect(container.firstChild?.childNodes.length ?? 0).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a label for each filter group', () => {
|
it('renders a label for each filter group', () => {
|
||||||
filterManager.setGroups([
|
filterManager.setGroups([
|
||||||
{ id: 'cat', label: 'Category', properties: [] },
|
{ id: 'cat', label: 'Categories', properties: [] },
|
||||||
{ id: 'prov', label: 'Provider', properties: [] },
|
{ id: 'prov', label: 'Font Providers', properties: [] },
|
||||||
]);
|
]);
|
||||||
render(Filters);
|
render(Filters);
|
||||||
expect(screen.getByText('Category')).toBeInTheDocument();
|
expect(screen.getByText('Categories')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Provider')).toBeInTheDocument();
|
expect(screen.getByText('Font Providers')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders filter properties within groups', () => {
|
it('renders filter properties within groups', () => {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ describe('ComboControl', () => {
|
|||||||
it('opens popover with vertical slider on trigger click', async () => {
|
it('opens popover with vertical slider on trigger click', async () => {
|
||||||
render(ComboControl, { control: makeControl(50), controlLabel: 'Size control' });
|
render(ComboControl, { control: makeControl(50), controlLabel: 'Size control' });
|
||||||
expect(screen.queryByRole('slider')).not.toBeInTheDocument();
|
expect(screen.queryByRole('slider')).not.toBeInTheDocument();
|
||||||
await fireEvent.click(screen.getByLabelText('Size control'));
|
await fireEvent.click(screen.getByText('Size control'));
|
||||||
await waitFor(() => expect(screen.getByRole('slider')).toBeInTheDocument());
|
await waitFor(() => expect(screen.getByRole('slider')).toBeInTheDocument());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [svelte()],
|
||||||
|
|
||||||
|
test: {
|
||||||
|
environment: 'jsdom',
|
||||||
|
include: ['src/**/*.svelte.test.ts'],
|
||||||
|
exclude: ['node_modules', 'dist', 'e2e', '.storybook'],
|
||||||
|
restoreMocks: true,
|
||||||
|
setupFiles: ['./vitest.setup.component.ts', './vitest.setup.jsdom.ts'],
|
||||||
|
globals: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
conditions: ['browser'],
|
||||||
|
alias: {
|
||||||
|
$lib: path.resolve(__dirname, './src/lib'),
|
||||||
|
$app: path.resolve(__dirname, './src/app'),
|
||||||
|
$shared: path.resolve(__dirname, './src/shared'),
|
||||||
|
$entities: path.resolve(__dirname, './src/entities'),
|
||||||
|
$features: path.resolve(__dirname, './src/features'),
|
||||||
|
$routes: path.resolve(__dirname, './src/routes'),
|
||||||
|
$widgets: path.resolve(__dirname, './src/widgets'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { queryClient } from '$shared/api/queryClient';
|
||||||
import * as matchers from '@testing-library/jest-dom/matchers';
|
import * as matchers from '@testing-library/jest-dom/matchers';
|
||||||
import { cleanup } from '@testing-library/svelte';
|
import { cleanup } from '@testing-library/svelte';
|
||||||
import {
|
import {
|
||||||
@@ -13,6 +14,7 @@ expect.extend(matchers);
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
queryClient.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mock window.matchMedia for components that use it
|
// Mock window.matchMedia for components that use it
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
|
// jsdom lacks ResizeObserver
|
||||||
|
global.ResizeObserver = class {
|
||||||
|
observe = vi.fn();
|
||||||
|
unobserve = vi.fn();
|
||||||
|
disconnect = vi.fn();
|
||||||
|
} as unknown as typeof ResizeObserver;
|
||||||
|
|
||||||
|
// jsdom lacks Web Animations API
|
||||||
|
Element.prototype.animate = vi.fn().mockReturnValue({
|
||||||
|
onfinish: null,
|
||||||
|
cancel: vi.fn(),
|
||||||
|
finish: vi.fn(),
|
||||||
|
pause: vi.fn(),
|
||||||
|
play: vi.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// jsdom lacks SVG geometry methods
|
||||||
|
SVGElement.prototype.getTotalLength = vi.fn(() => 0);
|
||||||
|
|
||||||
|
// Robust localStorage mock for jsdom environment
|
||||||
|
const localStorageMock = (() => {
|
||||||
|
let store: Record<string, string> = {};
|
||||||
|
return {
|
||||||
|
getItem: vi.fn((key: string) => store[key] || null),
|
||||||
|
setItem: vi.fn((key: string, value: string) => {
|
||||||
|
store[key] = value.toString();
|
||||||
|
}),
|
||||||
|
removeItem: vi.fn((key: string) => {
|
||||||
|
delete store[key];
|
||||||
|
}),
|
||||||
|
clear: vi.fn(() => {
|
||||||
|
store = {};
|
||||||
|
}),
|
||||||
|
key: vi.fn((index: number) => Object.keys(store)[index] || null),
|
||||||
|
get length() {
|
||||||
|
return Object.keys(store).length;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'localStorage', {
|
||||||
|
value: localStorageMock,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user