| name | testing |
| description | Use this skill when writing, running, or debugging tests for the RingCentral App Connect project. Covers Jest configuration, test patterns, mocking strategies, and test utilities. |
Testing Guide
Test Structure
rc-unified-crm-extension/
โโโ packages/core/test/ # Core package unit tests
โ โโโ handlers/ # Handler tests
โ โโโ lib/ # Library tests
โ โโโ models/ # Model tests
โโโ tests/ # Root-level integration tests
โ โโโ connectors/ # Connector-specific tests
โ โโโ fixtures/ # Test fixtures
โ โโโ *.test.js # Integration test files
โโโ jest.config.js # Root Jest config
Running Tests
npm test
npm run test:root
npm run test --workspace=@app-connect/core
npm run test-coverage
npx jest path/to/test.js
npx jest --watch
Jest Configuration
Root jest.config.js:
module.exports = {
testEnvironment: 'node',
testMatch: ['**/tests/**/*.test.js'],
forceExit: true,
};
Core package packages/core/jest.config.js:
module.exports = {
testEnvironment: 'node',
testMatch: ['**/test/**/*.test.js'],
};
Test Patterns
Unit Test Example
const { describe, it, expect, beforeEach, afterEach, jest } = require('@jest/globals');
describe('FunctionName', () => {
beforeEach(() => {
});
afterEach(() => {
jest.resetAllMocks();
});
it('should do something when condition', () => {
const input = { ... };
const result = functionUnderTest(input);
expect(result).toEqual(expected);
});
it('should handle error case', () => {
expect(() => functionUnderTest(badInput)).toThrow('Expected error');
});
});
Mocking with Nock (HTTP requests)
const nock = require('nock');
describe('API integration', () => {
afterEach(() => {
nock.cleanAll();
});
it('should fetch data from API', async () => {
nock('https://api.example.com')
.get('/endpoint')
.reply(200, { data: 'response' });
const result = await fetchData();
expect(result.data).toBe('response');
});
it('should handle API errors', async () => {
nock('https://api.example.com')
.get('/endpoint')
.reply(500, { error: 'Server error' });
await expect(fetchData()).rejects.toThrow();
});
});
Mocking Modules
jest.mock('@app-connect/core/lib/logger', () => ({
info: jest.fn(),
error: jest.fn(),
warn: jest.fn()
}));
jest.mock('@app-connect/core/models/userModel', () => ({
UserModel: {
findByPk: jest.fn()
}
}));
const { UserModel } = require('@app-connect/core/models/userModel');
UserModel.findByPk.mockResolvedValue({ id: '123', name: 'Test' });
Testing Handlers
const supertest = require('supertest');
const { createCoreApp } = require('@app-connect/core');
describe('Contact Handler', () => {
let app;
beforeAll(() => {
app = createCoreApp({ skipDatabaseInit: true });
});
it('should find contact by phone', async () => {
const response = await supertest(app)
.get('/contact')
.query({ phoneNumber: '+1234567890' })
.set('Authorization', 'Bearer token');
expect(response.status).toBe(200);
expect(response.body.matchedContactInfo).toBeDefined();
});
});
Testing Connectors
const nock = require('nock');
const connector = require('../../src/connectors/pipedrive');
describe('Pipedrive Connector', () => {
const mockUser = {
id: '123-pipedrive',
hostname: 'company.pipedrive.com',
accessToken: 'mock-token'
};
afterEach(() => {
nock.cleanAll();
});
describe('findContact', () => {
it('should find contacts by phone number', async () => {
nock('https://company.pipedrive.com')
.get('/api/v2/persons/search')
.query({ term: '1234567890', fields: 'phone' })
.reply(200, {
data: {
items: [{ item: { id: 1, name: 'Test Contact' } }]
}
});
const result = await connector.findContact({
user: mockUser,
authHeader: 'Bearer mock-token',
phoneNumber: '+1234567890'
});
expect(result.successful).toBe(true);
expect(result.matchedContactInfo).toHaveLength(2);
});
});
});
Test Utilities
Setup File (tests/setup.js)
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL = 'sqlite::memory:';
global.createMockUser = (overrides = {}) => ({
id: 'test-user-id',
accessToken: 'mock-access-token',
refreshToken: 'mock-refresh-token',
hostname: 'test.crm.com',
...overrides
});
Fixtures (tests/fixtures/)
module.exports = {
basicCallLog: {
sessionId: 'session-123',
startTime: Date.now(),
duration: 120,
direction: 'Inbound',
from: { phoneNumber: '+1234567890' },
to: { phoneNumber: '+0987654321' }
}
};
Coverage
Run coverage report:
npm run test-coverage
Coverage output in coverage/ directory:
lcov-report/index.html - HTML report
coverage-final.json - JSON data
lcov.info - LCOV format
Best Practices
- Isolate tests - Each test should be independent
- Mock external APIs - Use nock for HTTP mocking
- Clean up - Always clean mocks in
afterEach
- Descriptive names -
it('should return 404 when contact not found')
- Test edge cases - Empty arrays, null values, error states
- Avoid testing implementation - Test behavior, not internals