npm package: @flightdev/testing
Description: Testing utilities for Flight Framework applications
Latest version: 0.0.6

# @flightdev/testing

Testing utilities for Flight Framework. Test components, routes, and server actions with ease.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Component Testing](#component-testing)
- [Route Testing](#route-testing)
- [Server Action Testing](#server-action-testing)
- [API Reference](#api-reference)
- [License](#license)

---

## Installation

```bash
npm install -D @flightdev/testing
```

---

## Quick Start

```typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { flightTestPlugin } from '@flightdev/testing/vitest';

export default defineConfig({
    plugins: [flightTestPlugin()],
    test: {
        environment: 'jsdom',
    },
});
```

---

## Component Testing

### React Components

```tsx
import { render, screen } from '@flightdev/testing/react';
import { Button } from './Button';

test('renders button with text', () => {
    render(<Button>Click me</Button>);
    expect(screen.getByRole('button')).toHaveTextContent('Click me');
});

test('calls onClick when clicked', async () => {
    const onClick = vi.fn();
    const { user } = render(<Button onClick={onClick}>Click</Button>);
    
    await user.click(screen.getByRole('button'));
    expect(onClick).toHaveBeenCalled();
});
```

### Vue Components

```typescript
import { mount } from '@flightdev/testing/vue';
import Button from './Button.vue';

test('renders button', async () => {
    const wrapper = mount(Button, {
        props: { label: 'Click me' },
    });
    
    expect(wrapper.text()).toContain('Click me');
    await wrapper.trigger('click');
});
```

---

## Route Testing

### Test Loaders

```typescript
import { createTestContext } from '@flightdev/testing';
import { loader } from './blog/[slug].page';

test('loader returns post data', async () => {
    const ctx = createTestContext({
        params: { slug: 'hello-world' },
    });
    
    const data = await loader(ctx);
    expect(data.post.title).toBe('Hello World');
});

test('loader throws 404 for missing post', async () => {
    const ctx = createTestContext({
        params: { slug: 'does-not-exist' },
    });
    
    await expect(loader(ctx)).rejects.toThrow('Not Found');
});
```

### Test Actions

```typescript
import { createTestContext } from '@flightdev/testing';
import { action } from './contact.page';

test('action creates contact submission', async () => {
    const ctx = createTestContext({
        method: 'POST',
        body: new FormData([
            ['email', 'test@example.com'],
            ['message', 'Hello'],
        ]),
    });
    
    const response = await action(ctx);
    expect(response.status).toBe(200);
});
```

---

## Server Action Testing

```typescript
import { createActionTest } from '@flightdev/testing';
import { submitForm } from './actions';

test('submitForm validates input', async () => {
    const testAction = createActionTest(submitForm);
    
    // Test with invalid data
    const result = await testAction({ email: 'invalid' });
    expect(result.error).toBe('Invalid email');
    
    // Test with valid data
    const success = await testAction({ email: 'test@example.com' });
    expect(success.data).toBeDefined();
});
```

---

## API Reference

### createTestContext

Create a mock request context for testing loaders and actions.

```typescript
const ctx = createTestContext({
    url: '/blog/hello',
    method: 'GET',
    params: { slug: 'hello' },
    searchParams: { page: '1' },
    headers: { 'Authorization': 'Bearer token' },
    cookies: { session: 'abc123' },
    body: { data: 'value' },
});
```

### render (React)

Render a component with Flight context.

```typescript
const { user, rerender, unmount } = render(
    <Component prop="value" />,
    {
        route: '/current-path',
        loader: { data: 'from loader' },
    }
);
```

### Mock Utilities

```typescript
import { mockFetch, mockLoader, mockAction } from '@flightdev/testing';

// Mock fetch globally
mockFetch({
    '/api/users': { users: [] },
});

// Mock a loader
mockLoader('./blog/[slug].page', async () => ({
    post: { title: 'Mocked' },
}));
```

---

## License

MIT