Test your Forest customizations locally without connecting to Forest servers.
The testing library is available for the @forestadmin/agent Node.js agent.
Installation
npm install --save-dev @forestadmin/agent-testing
Quick start
The testing library provides two main functions:
createForestServerSandbox(port) starts a local mock server that simulates Forest servers.
createAgentTestClient(options) creates a test client to interact with your agent.
import {
createAgentTestClient,
createForestServerSandbox,
} from '@forestadmin/agent-testing';
describe('My Agent', () => {
let sandbox, client;
beforeAll(async () => {
// Start mock server
sandbox = await createForestServerSandbox(3001);
// Connect test client
client = await createAgentTestClient({
serverUrl: 'http://localhost:3001',
agentUrl: 'http://localhost:3310',
agentSchemaPath: './.forestadmin-schema.json',
agentForestEnvSecret: process.env.FOREST_ENV_SECRET,
agentForestAuthSecret: process.env.FOREST_AUTH_SECRET,
});
});
afterAll(async () => {
await sandbox?.stop();
});
it('should list users', async () => {
const users = await client.collection('users').list();
expect(users.length).toBeGreaterThan(0);
});
it('should execute action', async () => {
const action = await client
.collection('orders')
.action('Apply discount', { recordId: 1 });
await action.getFieldNumber('discount').fill(10);
const result = await action.execute();
expect(result.success).toBe(true);
});
});
Collections
Test CRUD operations and hooks.
List
Test that filters return the correct records.
// Fetch admins
const admins = await client.collection('users').list({
filters: { field: 'role', operator: 'Equal', value: 'admin' },
});
// Check all have role 'admin'
expect(admins.every(u => u.role === 'admin')).toBe(true);
Create
// Create user
const user = await client.collection('users').create({ email: 'john@example.com' });
// Fetch to verify
const [created] = await client.collection('users').list({
filters: { field: 'id', operator: 'Equal', value: user.id },
});
expect(created.email).toBe('john@example.com');
Update
// Update email
await client.collection('users').update(userId, { email: 'new@example.com' });
const [user] = await client.collection('users').list({
filters: { field: 'id', operator: 'Equal', value: userId },
});
expect(user.email).toBe('new@example.com');
Delete
// Delete user
await client.collection('users').delete(userId);
const users = await client.collection('users').list({
filters: { field: 'id', operator: 'Equal', value: userId },
});
expect(users).toHaveLength(0);
Actions
Test actions and form behavior.
// Get action
const action = await client.collection('orders').action('Refund', { recordId: 1 });
// Fill form
await action.getFieldNumber('amount').fill(100);
await action.getFieldString('reason').fill('Customer request');
// Execute
const result = await action.execute();
expect(result.success).toBe(true);
Test fields that appear based on other field values.
const action = await client.collection('orders').action('Refund', { recordId: 1 });
// Check field is hidden
expect(action.doesFieldExist('manager_approval')).toBe(false);
// Fill amount > threshold
await action.getFieldNumber('amount').fill(500);
// Check field now appears
expect(action.doesFieldExist('manager_approval')).toBe(true);
Field properties
Test field validation rules.
const action = await client.collection('users').action('Update', { recordId: 1 });
// Check required
expect(action.getFieldString('email').isRequired()).toBe(true);
// Check read-only
expect(action.getFieldNumber('age').isReadOnly()).toBe(false);
Computed fields
// Fetch user
const [user] = await client.collection('users').list<{ fullName: string }>();
// Check computed value
expect(user.fullName).toBe('John Doe');
Segments
// Fetch from segment
const minors = await client.collection('users').segment('minors').list();
// Check filter works
expect(minors.every(u => u.age < 18)).toBe(true);
Charts
// Fetch value chart
const chart = await client.valueChart('totalRevenue');
expect(chart.countCurrent).toBeGreaterThan(0);
// Fetch distribution chart
const distribution = await client.distributionChart('ordersByStatus');
expect(distribution).toContainEqual({ key: 'pending', value: expect.any(Number) });