Added tests

This commit is contained in:
Hein 2025-10-15 10:36:18 +02:00
parent 9c11b609dc
commit f058336597
10 changed files with 913 additions and 8 deletions

View File

@ -32,7 +32,7 @@ npm install @warkypublic/oranguru
This package requires the following peer dependencies: This package requires the following peer dependencies:
```bash ```bash
npm install react@">= 19.0.0" zustand@">= 5.0.0" @mantine/core@"^8.3.1" @mantine/hooks@"^8.3.1" @warkypublic/artemis-kit@"^1.0.10" @warkypublic/zustandsyncstore@"^0.0.2" use-sync-external-store@">= 1.4.0" npm install react@">= 19.0.0" zustand@">= 5.0.0" @mantine/core@"^8.3.1" @mantine/hooks@"^8.3.1" @warkypublic/artemis-kit@"^1.0.10" @warkypublic/zustandsyncstore@"^0.0.4" use-sync-external-store@">= 1.4.0"
``` ```
## Usage ## Usage

View File

@ -7,11 +7,12 @@ import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh'; import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import perfectionist from 'eslint-plugin-perfectionist'; import perfectionist from 'eslint-plugin-perfectionist';
import { globalIgnores } from 'eslint/config';
export default tseslint.config( export default tseslint.config(
{
ignores: ['dist'],
},
[ [
globalIgnores(['dist']),
{ {
files: ['**/*.{ts,tsx}'], files: ['**/*.{ts,tsx}'],
extends: [ extends: [

View File

@ -8,6 +8,10 @@
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint ./src", "lint": "eslint ./src",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui",
"clean": "rm -rf node_modules && rm -rf dist ", "clean": "rm -rf node_modules && rm -rf dist ",
"preview": "vite preview", "preview": "vite preview",
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
@ -57,6 +61,9 @@
"@changesets/cli": "^2.29.7", "@changesets/cli": "^2.29.7",
"@eslint/js": "^9.35.0", "@eslint/js": "^9.35.0",
"@storybook/react-vite": "^9.1.7", "@storybook/react-vite": "^9.1.7",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^24.4.0", "@types/node": "^24.4.0",
"@types/react": "^19.1.13", "@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9", "@types/react-dom": "^19.1.9",
@ -70,6 +77,7 @@
"eslint-plugin-storybook": "^9.1.7", "eslint-plugin-storybook": "^9.1.7",
"global": "^4.4.0", "global": "^4.4.0",
"globals": "^16.4.0", "globals": "^16.4.0",
"jsdom": "^27.0.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-eslint": "^16.4.2", "prettier-eslint": "^16.4.2",
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
@ -78,7 +86,8 @@
"typescript-eslint": "^8.43.0", "typescript-eslint": "^8.43.0",
"vite": "^7.1.5", "vite": "^7.1.5",
"vite-plugin-dts": "^4.5.4", "vite-plugin-dts": "^4.5.4",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4"
}, },
"peerDependencies": { "peerDependencies": {
"@mantine/core": "^8.3.1", "@mantine/core": "^8.3.1",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
import { MantineProvider } from '@mantine/core'
import { render, screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { Gridler } from './Gridler'
// Mock the complex sub-components
vi.mock('./GridlerDataGrid', () => ({
GridlerDataGrid: () => <div data-testid="gridler-data-grid">Data Grid</div>
}))
vi.mock('../MantineBetterMenu', () => ({
MantineBetterMenusProvider: ({ children }: { children: React.ReactNode }) => (
<div data-testid="menu-provider">{children}</div>
)
}))
// Mock the Store Provider
vi.mock('./components/Store', () => ({
Provider: ({ children, uniqueid }: { children: React.ReactNode; uniqueid: string }) => (
<div data-testid={`store-provider-${uniqueid}`}>{children}</div>
)
}))
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider>
{children}
</MantineProvider>
)
describe('Gridler', () => {
const defaultProps = {
cols: [
{ key: 'id', title: 'ID' },
{ key: 'name', title: 'Name' }
],
uniqueid: 'test-grid'
}
it('renders without crashing', () => {
render(
<TestWrapper>
<Gridler {...defaultProps} />
</TestWrapper>
)
expect(screen.getByTestId('gridler-data-grid')).toBeInTheDocument()
})
it('wraps content with MantineBetterMenusProvider', () => {
render(
<TestWrapper>
<Gridler {...defaultProps} />
</TestWrapper>
)
expect(screen.getByTestId('menu-provider')).toBeInTheDocument()
})
it('creates store provider with unique ID', () => {
render(
<TestWrapper>
<Gridler {...defaultProps} />
</TestWrapper>
)
expect(screen.getByTestId('store-provider-test-grid')).toBeInTheDocument()
})
it('renders children when provided', () => {
render(
<TestWrapper>
<Gridler {...defaultProps}>
<div data-testid="test-child">Custom Child</div>
</Gridler>
</TestWrapper>
)
expect(screen.getByTestId('test-child')).toBeInTheDocument()
})
it('handles different uniqueid prop', () => {
render(
<TestWrapper>
<Gridler {...defaultProps} uniqueid="different-grid" />
</TestWrapper>
)
expect(screen.getByTestId('store-provider-different-grid')).toBeInTheDocument()
})
})

View File

@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest'
import { range } from './range'
describe('range utility', () => {
describe('single argument (end only)', () => {
it('creates range from 0 to n-1', () => {
expect(range(5)).toEqual([0, 1, 2, 3, 4])
})
it('handles zero', () => {
expect(range(0)).toEqual([])
})
it('handles one', () => {
expect(range(1)).toEqual([0])
})
it('handles negative numbers', () => {
expect(range(-5)).toEqual([])
})
})
describe('two arguments (start, end)', () => {
it('creates range from start to end-1', () => {
expect(range(2, 7)).toEqual([2, 3, 4, 5, 6])
})
it('handles start equal to end', () => {
expect(range(5, 5)).toEqual([])
})
it('handles start greater than end', () => {
expect(range(7, 2)).toEqual([])
})
})
describe('three arguments (start, end, step)', () => {
it('creates range with positive step', () => {
expect(range(0, 10, 2)).toEqual([0, 2, 4, 6, 8])
})
it('creates range with negative step', () => {
expect(range(10, 0, -2)).toEqual([10, 8, 6, 4, 2])
})
it('throws error for zero step', () => {
expect(() => range(0, 10, 0)).toThrow('Step cannot be zero')
})
})
})

View File

@ -0,0 +1,55 @@
import { MantineProvider } from '@mantine/core'
import { render, screen } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { MantineBetterMenusProvider } from './MantineBetterMenu'
// Mock the MenuRenderer component since it likely has complex portal logic
vi.mock('./MenuRenderer', () => ({
MenuRenderer: () => <div data-testid="menu-renderer">Menu Renderer</div>
}))
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
<MantineProvider>
{children}
</MantineProvider>
)
describe('MantineBetterMenusProvider', () => {
it('renders children correctly', () => {
render(
<TestWrapper>
<MantineBetterMenusProvider>
<div data-testid="test-child">Test Child</div>
</MantineBetterMenusProvider>
</TestWrapper>
)
expect(screen.getByTestId('test-child')).toBeInTheDocument()
})
it('renders MenuRenderer component', () => {
render(
<TestWrapper>
<MantineBetterMenusProvider>
<div>Test</div>
</MantineBetterMenusProvider>
</TestWrapper>
)
expect(screen.getByTestId('menu-renderer')).toBeInTheDocument()
})
it('accepts providerID prop', () => {
render(
<TestWrapper>
<MantineBetterMenusProvider providerID="test-provider">
<div>Test</div>
</MantineBetterMenusProvider>
</TestWrapper>
)
// Component should render without errors
expect(screen.getByTestId('menu-renderer')).toBeInTheDocument()
})
})

View File

@ -0,0 +1,100 @@
import { act, renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it } from 'vitest'
import { MantineBetterMenusStoreProvider, useMantineBetterMenus } from './Store'
// Mock dependencies
vi.mock('@warkypublic/artemis-kit', () => ({
getUUID: () => 'test-uuid-123'
}))
const createWrapper = () => {
return ({ children }: { children: React.ReactNode }) => (
<MantineBetterMenusStoreProvider>
{children}
</MantineBetterMenusStoreProvider>
)
}
describe('MantineBetterMenus Store', () => {
let wrapper: ReturnType<typeof createWrapper>
beforeEach(() => {
wrapper = createWrapper()
})
it('initializes with empty menus array', () => {
const { result } = renderHook(() => useMantineBetterMenus(), { wrapper })
expect(result.current.menus).toEqual([])
})
it('can show a menu', () => {
const { result } = renderHook(() => useMantineBetterMenus(), { wrapper })
act(() => {
result.current.show('test-menu', {
items: [{ label: 'Test Item' }],
x: 100,
y: 200
})
})
expect(result.current.menus).toHaveLength(1)
expect(result.current.menus[0]).toMatchObject({
id: 'test-menu',
items: [{ label: 'Test Item' }],
visible: true,
x: 100,
y: 200
})
})
it('can hide a menu', () => {
const { result } = renderHook(() => useMantineBetterMenus(), { wrapper })
// First show a menu
act(() => {
result.current.show('test-menu', { x: 100, y: 200 })
})
expect(result.current.menus[0].visible).toBe(true)
// Then hide it
act(() => {
result.current.hide('test-menu')
})
expect(result.current.menus[0].visible).toBe(false)
})
it('can update instance state', () => {
const { result } = renderHook(() => useMantineBetterMenus(), { wrapper })
// Show a menu
act(() => {
result.current.show('test-menu', { x: 100, y: 200 })
})
// Update its position
act(() => {
result.current.setInstanceState('test-menu', 'x', 300)
})
expect(result.current.menus[0].x).toBe(300)
expect(result.current.menus[0].y).toBe(200) // Should remain unchanged
})
it('handles multiple menus', () => {
const { result } = renderHook(() => useMantineBetterMenus(), { wrapper })
act(() => {
result.current.show('menu-1', { x: 100, y: 200 })
result.current.show('menu-2', { x: 300, y: 400 })
})
expect(result.current.menus).toHaveLength(2)
expect(result.current.menus.find(m => m.id === 'menu-1')).toBeDefined()
expect(result.current.menus.find(m => m.id === 'menu-2')).toBeDefined()
})
})

30
src/test/setup.ts Normal file
View File

@ -0,0 +1,30 @@
import '@testing-library/jest-dom'
// Mock window.matchMedia for Mantine components
Object.defineProperty(window, 'matchMedia', {
value: vi.fn().mockImplementation(query => ({
addEventListener: vi.fn(),
addListener: vi.fn(), // deprecated
dispatchEvent: vi.fn(),
matches: false,
media: query,
onchange: null,
removeEventListener: vi.fn(),
removeListener: vi.fn(), // deprecated
})),
writable: true,
})
// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}))
// Mock IntersectionObserver
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}))

12
vitest.config.ts Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
globals: true,
css: true,
},
})