npm package: @kodeme-io/next-core-testing
Description: Testing utilities and helpers for next-core applications
Author: ABC Food Development Team
Latest version: 0.8.4

# @kodeme-io/next-core-testing

Testing utilities and helpers for next-core applications. Provides test utilities, mock data factories, and a shared Jest configuration.

## Installation

```bash
npm install --save-dev @kodeme-io/next-core-testing @testing-library/react @testing-library/jest-dom jest jest-environment-jsdom
```

## Features

- 🧪 **Test Utilities**: Pre-configured `render` function with providers
- 🏭 **Mock Factories**: Generate realistic test data
- 🎭 **Mock Implementations**: Mock data clients and adapters
- ⚙️ **Jest Configuration**: Shared config for consistency
- 📝 **TypeScript**: Full type safety

## Usage

### Test Utilities

```tsx
import { render, screen, userEvent } from '@kodeme-io/next-core-testing'

test('renders component', async () => {
  render(<MyComponent />)

  const button = screen.getByRole('button', { name: /submit/i })
  await userEvent.click(button)

  expect(screen.getByText('Success')).toBeInTheDocument()
})
```

### Mock Data Factories

```tsx
import { createMockSaleOrder, createMockPartner } from '@kodeme-io/next-core-testing'

test('displays order list', () => {
  // Create 5 mock orders with default values
  const orders = createMockSaleOrder(5)

  render(<OrderList orders={orders} />)
  expect(screen.getByText('SO001')).toBeInTheDocument()
})

test('displays VIP orders', () => {
  // Create orders with custom overrides
  const vipOrders = createMockSaleOrder(3, {
    state: 'sale',
    amount_total: 5000000
  })

  render(<OrderList orders={vipOrders} />)
  expect(screen.getAllByText('sale')).toHaveLength(3)
})
```

### Mock Data Client

```tsx
import { mockDataClient, createMockSaleOrder } from '@kodeme-io/next-core-testing'

beforeEach(() => {
  // Setup mock data
  const orders = createMockSaleOrder(10, { state: 'sale' })
  mockDataClient.setMockData('sale.order', orders)
})

afterEach(() => {
  // Clean up after each test
  mockDataClient.clearMockData()
})

test('fetches orders', async () => {
  const orders = await mockDataClient.findMany('sale.order')
  expect(orders).toHaveLength(10)
})

test('filters orders', async () => {
  const confirmedOrders = await mockDataClient.search('sale.order', {
    where: { state: 'sale' }
  })
  expect(confirmedOrders).toHaveLength(10)
})
```

### Mock Odoo Adapter

```tsx
import { mockOdooAdapter } from '@kodeme-io/next-core-testing'

beforeEach(() => {
  mockOdooAdapter.setMockResponse('sale.order', 'search_read', [
    { id: 1, name: 'SO001', state: 'sale' }
  ])
})

afterEach(() => {
  mockOdooAdapter.clearMockResponses()
})

test('calls Odoo API', async () => {
  const result = await mockOdooAdapter.search_read(
    'sale.order',
    [],
    ['id', 'name', 'state']
  )

  expect(result).toHaveLength(1)
  expect(result[0].name).toBe('SO001')
})
```

## Available Factories

All factories accept `count` and `overrides` parameters:

```typescript
createMockFactory(count: number = 1, overrides: Partial<Type> = {})
```

### `createMockSaleOrder`

Creates mock `SaleOrder` records with realistic defaults.

**Default fields:**
- `id`: Sequential (1, 2, 3...)
- `name`: SO001, SO002, SO003...
- `partner_id`: 1
- `state`: 'sale'
- `amount_total`: 1100000
- All required fields populated

**Example:**
```tsx
// Single order
const order = createMockSaleOrder()[0]

// Multiple orders
const orders = createMockSaleOrder(10)

// With overrides
const draftOrders = createMockSaleOrder(5, { state: 'draft' })
```

### `createMockSaleOrderLine`

Creates mock `SaleOrderLine` records.

**Example:**
```tsx
const lines = createMockSaleOrderLine(3, {
  order_id: 1,
  product_uom_qty: 10
})
```

### `createMockPartner`

Creates mock `Partner` records (customers/suppliers).

**Example:**
```tsx
const customers = createMockPartner(5, {
  customer_type: 'vip',
  is_company: true
})
```

### `createMockProduct`

Creates mock `Product` records.

**Example:**
```tsx
const products = createMockProduct(20, {
  sale_ok: true,
  qty_available: 100
})
```

### `createMockEmployee`

Creates mock `Employee` records.

**Example:**
```tsx
const staff = createMockEmployee(10, {
  department: 'Sales',
  active: true
})
```

### `createMockInventory`

Creates mock `Inventory` records.

**Example:**
```tsx
const stock = createMockInventory(50, {
  available_quantity: 100,
  location_name: 'Main Warehouse'
})
```

## Jest Configuration

### Setup

1. **Create `jest.config.js` in your app root:**

```js
const baseConfig = require('@kodeme-io/next-core-testing/jest-config')

module.exports = {
  ...baseConfig,
  // Your app-specific overrides
  displayName: 'next-sfa',
}
```

2. **Create `jest.setup.js` in your app root:**

```js
import '@testing-library/jest-dom'

// Add any global test setup here
```

3. **Add test script to `package.json`:**

```json
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}
```

### Run Tests

```bash
# Run all tests
npm test

# Watch mode
npm run test:watch

# With coverage
npm run test:coverage
```

## Utility Functions

### `createSpy()`

Create a simple spy to track function calls.

```tsx
const spy = createSpy()

spy('arg1', 'arg2')
spy('arg3')

expect(spy.calls).toHaveLength(2)
expect(spy.calls[0]).toEqual(['arg1', 'arg2'])
expect(spy.calls[1]).toEqual(['arg3'])

spy.reset() // Clear call history
```

### `waitForCondition()`

Wait for a condition to be true.

```tsx
await waitForCondition(
  () => element.textContent === 'Loaded',
  5000 // timeout in ms
)
```

### `mockLocalStorage()`

Mock localStorage for testing.

```tsx
beforeEach(() => {
  global.localStorage = mockLocalStorage()
})

test('saves to localStorage', () => {
  localStorage.setItem('key', 'value')
  expect(localStorage.getItem('key')).toBe('value')
})
```

### `createMockFetch()`

Mock fetch API for testing.

```tsx
const mockFetch = createMockFetch()
mockFetch.setResponse('/api/orders', { data: [] })
global.fetch = mockFetch.fetch

test('fetches orders', async () => {
  const response = await fetch('/api/orders')
  const data = await response.json()
  expect(data).toEqual({ data: [] })
})
```

## Best Practices

### 1. Use Factories for Test Data

✅ **Do:**
```tsx
const orders = createMockSaleOrder(5, { state: 'sale' })
```

❌ **Don't:**
```tsx
const orders = [
  { id: 1, name: 'SO001', partner_id: 1, state: 'sale', /* 20 more fields */ },
  { id: 2, name: 'SO002', partner_id: 1, state: 'sale', /* 20 more fields */ },
  // ...
]
```

### 2. Clean Up After Tests

```tsx
afterEach(() => {
  mockDataClient.clearMockData()
  mockOdooAdapter.clearMockResponses()
})
```

### 3. Use Descriptive Test Names

✅ **Do:**
```tsx
test('displays error message when order creation fails', () => {
  // ...
})
```

❌ **Don't:**
```tsx
test('test1', () => {
  // ...
})
```

### 4. Test User Interactions

```tsx
import { userEvent } from '@kodeme-io/next-core-testing'

test('submits form on button click', async () => {
  render(<OrderForm />)

  await userEvent.type(screen.getByLabelText('Customer'), 'John Doe')
  await userEvent.click(screen.getByRole('button', { name: /submit/i }))

  expect(screen.getByText('Order created')).toBeInTheDocument()
})
```

## TypeScript Support

All exports are fully typed. Import types as needed:

```tsx
import type { SaleOrder, Partner } from '@next-core/shared-mock-data'
import { createMockSaleOrder } from '@kodeme-io/next-core-testing'

const orders: SaleOrder[] = createMockSaleOrder(5)
```

## Related Packages

- `@kodeme-io/next-core-ui` - UI components
- `@kodeme-io/next-core-client` - Data client
- `@kodeme-io/next-core-hooks` - React hooks
- `@next-core/shared-mock-data` - Type definitions

## License

MIT

## Contributing

Contributions welcome! Please see the main [next-core repository](https://github.com/your-org/next-core) for guidelines.