- Implemented FullPagePageIndex component with mock data for database clusters. - Added pagination and bulk actions for managing database clusters. - Created navigation context hooks for detail, edit, duplicate, and create actions. - Introduced a new store for managing state in full-page and single-page modules. feat: add navigation localization files - Added English and Indonesian localization files for navigation menu items. - Included translations for various modules including CRM, Sales, Supply Chain, and more. feat: create system information shortcuts component - Developed Shortcut component to display keyboard shortcuts with search functionality. - Implemented System component to show placeholder information when system details are unavailable. - Added localization for shortcuts and system information in English and Indonesian. feat: implement global theme store - Created a Zustand store for managing theme color scheme with localStorage persistence. feat: add module page header component - Developed ModulePageHeader component for consistent page header across modules. - Included breadcrumb navigation, title, description, and action buttons. feat: define default privileges for enterprise module - Established default privileges for CRUD operations and other actions in the enterprise module.
657 lines
23 KiB
TypeScript
657 lines
23 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
import type { BaseEntity } from './types';
|
|
import type { IDataTransformer } from './base-data.transformer';
|
|
import { CommonRemoteDataServices } from './common-remote.data-services';
|
|
import { BaseDataTransformer } from './base-data.transformer';
|
|
|
|
// ─── Mock AxiosInstance ─────────────────────────────────────────
|
|
|
|
function createMockHttpClient(): AxiosInstance {
|
|
return {
|
|
request: vi.fn().mockResolvedValue({
|
|
data: [{ id: '1', name: 'Test' }],
|
|
status: 200,
|
|
}),
|
|
// Satisfy the AxiosInstance shape (unused properties)
|
|
defaults: {} as AxiosInstance['defaults'],
|
|
interceptors: {
|
|
request: { use: vi.fn(), eject: vi.fn(), clear: vi.fn() },
|
|
response: { use: vi.fn(), eject: vi.fn(), clear: vi.fn() },
|
|
},
|
|
getUri: vi.fn(),
|
|
get: vi.fn(),
|
|
delete: vi.fn(),
|
|
head: vi.fn(),
|
|
options: vi.fn(),
|
|
post: vi.fn(),
|
|
put: vi.fn(),
|
|
patch: vi.fn(),
|
|
postForm: vi.fn(),
|
|
putForm: vi.fn(),
|
|
patchForm: vi.fn(),
|
|
} as unknown as AxiosInstance;
|
|
}
|
|
|
|
// ─── Test Entity ────────────────────────────────────────────────
|
|
|
|
interface TestEntity extends BaseEntity {
|
|
bookingCode: string;
|
|
customerName: string;
|
|
}
|
|
|
|
// ─── Tests ──────────────────────────────────────────────────────
|
|
|
|
describe('BaseRemoteDataServices (via CommonRemoteDataServices)', () => {
|
|
let mockClient: AxiosInstance;
|
|
let services: CommonRemoteDataServices<TestEntity>;
|
|
|
|
beforeEach(() => {
|
|
mockClient = createMockHttpClient();
|
|
services = new CommonRemoteDataServices<TestEntity>(mockClient, {
|
|
apiUrl: '/bookings',
|
|
moduleKey: 'BOOKING',
|
|
});
|
|
});
|
|
|
|
// ── URL Interpolation ─────────────────────────────────────────
|
|
|
|
describe('URL interpolation', () => {
|
|
it('getMany() uses the base URL without params', async () => {
|
|
await services.getMany();
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings',
|
|
method: 'GET',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('getOne() replaces :id in the URL template', async () => {
|
|
await services.getOne('123');
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings/123',
|
|
method: 'GET',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('getOne() encodes special characters in ID', async () => {
|
|
await services.getOne('hello world');
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings/hello%20world',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('activate() resolves to /:id/active', async () => {
|
|
await services.activate('42');
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings/42/active',
|
|
method: 'PATCH',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('confirmData() resolves to /:id/confirm', async () => {
|
|
await services.confirmData('99');
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings/99/confirm',
|
|
method: 'PATCH',
|
|
}),
|
|
);
|
|
});
|
|
});
|
|
|
|
// ── Header Injection ──────────────────────────────────────────
|
|
|
|
describe('header injection', () => {
|
|
it('injects ex-module-key from moduleKey config', async () => {
|
|
await services.getMany();
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-key']).toBe('BOOKING');
|
|
});
|
|
|
|
it('injects ex-module-action from the descriptor action', async () => {
|
|
await services.getMany(); // VIEW action
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-action']).toBe('VIEW');
|
|
});
|
|
|
|
it('injects CREATE action for create()', async () => {
|
|
await services.create({ bookingCode: 'BK001', customerName: 'Test' } as Partial<TestEntity>);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-action']).toBe('CREATE');
|
|
});
|
|
|
|
it('injects EDIT action for edit()', async () => {
|
|
await services.edit('42', { customerName: 'Updated' } as Partial<TestEntity>);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-action']).toBe('EDIT');
|
|
});
|
|
|
|
it('injects DELETE action for delete()', async () => {
|
|
await services.delete('42');
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-action']).toBe('DELETE');
|
|
});
|
|
|
|
it('omits ex-module-key when moduleKey is not configured', async () => {
|
|
const noKeyServices = new CommonRemoteDataServices<TestEntity>(mockClient, {
|
|
apiUrl: '/items',
|
|
});
|
|
|
|
await noKeyServices.getMany();
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-key']).toBeUndefined();
|
|
// But action is always present
|
|
expect(requestArg.headers['ex-module-action']).toBe('VIEW');
|
|
});
|
|
|
|
it('preserves caller-provided headers alongside injected ones', async () => {
|
|
await services.getMany({
|
|
headers: { 'X-Custom-Header': 'custom-value' },
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-key']).toBe('BOOKING');
|
|
expect(requestArg.headers['ex-module-action']).toBe('VIEW');
|
|
expect(requestArg.headers['X-Custom-Header']).toBe('custom-value');
|
|
});
|
|
});
|
|
|
|
// ── Telemetry Context ─────────────────────────────────────────
|
|
|
|
describe('telemetry context passthrough', () => {
|
|
it('passes telemetryContext from getMany() config to the Axios request', async () => {
|
|
const telemetryContext = {
|
|
customSpanName: 'booking.list.fetch',
|
|
tags: { region: 'asia' },
|
|
pushEventOnSuccess: 'booking_list_loaded',
|
|
};
|
|
|
|
await services.getMany({ telemetryContext } as AxiosRequestConfig);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.telemetryContext).toEqual(telemetryContext);
|
|
});
|
|
|
|
it('passes telemetryContext via customRequest()', async () => {
|
|
const telemetryContext = {
|
|
customSpanName: 'custom.tax.calculate',
|
|
tags: { business: 'tax' },
|
|
};
|
|
|
|
await services.customRequest({
|
|
url: '/bookings/42/calculate-tax',
|
|
method: 'POST',
|
|
data: { items: [] },
|
|
telemetryContext,
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.telemetryContext).toEqual(telemetryContext);
|
|
});
|
|
|
|
it('customRequest() still injects ex-module-key header', async () => {
|
|
await services.customRequest({
|
|
url: '/bookings/42/calculate-tax',
|
|
method: 'POST',
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.headers['ex-module-key']).toBe('BOOKING');
|
|
});
|
|
});
|
|
|
|
// ── Response Shape ────────────────────────────────────────────
|
|
|
|
describe('response shape', () => {
|
|
it('returns { data, status } from the Axios response', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: { id: '42', bookingCode: 'BK042', customerName: 'Alice' },
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getOne<TestEntity>('42');
|
|
|
|
expect(result).toEqual({
|
|
data: { id: '42', bookingCode: 'BK042', customerName: 'Alice' },
|
|
status: 200,
|
|
});
|
|
});
|
|
|
|
it('create() passes data in the Axios request config', async () => {
|
|
const newBooking: Partial<TestEntity> = {
|
|
bookingCode: 'BK001',
|
|
customerName: 'Bob',
|
|
};
|
|
|
|
await services.create(newBooking);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.method).toBe('POST');
|
|
expect(requestArg.url).toBe('/bookings');
|
|
expect(requestArg.data).toEqual(newBooking);
|
|
});
|
|
});
|
|
|
|
// ── Batch Operations ──────────────────────────────────────────
|
|
|
|
describe('batch operations', () => {
|
|
it('batchDelete() sends ids in data payload', async () => {
|
|
await services.batchDelete(['1', '2', '3']);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.url).toBe('/bookings/batch-delete');
|
|
expect(requestArg.data).toEqual({ ids: ['1', '2', '3'] });
|
|
});
|
|
});
|
|
});
|
|
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// Data Transformer Integration Tests
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
// ─── Test DTO (snake_case API shape) ────────────────────────────
|
|
|
|
interface TestDTO {
|
|
id?: string;
|
|
booking_code: string;
|
|
customer_name: string;
|
|
}
|
|
|
|
interface TestEntity2 extends BaseEntity {
|
|
bookingCode: string;
|
|
customerName: string;
|
|
}
|
|
|
|
// ─── Concrete Transformer for Testing ───────────────────────────
|
|
|
|
class TestTransformer extends BaseDataTransformer<TestEntity2, TestDTO> {
|
|
transformToEntity(dto: TestDTO): TestEntity2 {
|
|
return {
|
|
id: dto.id,
|
|
bookingCode: dto.booking_code,
|
|
customerName: dto.customer_name,
|
|
};
|
|
}
|
|
|
|
transformToDTO(entity: TestEntity2): TestDTO {
|
|
return {
|
|
id: entity.id,
|
|
booking_code: entity.bookingCode,
|
|
customer_name: entity.customerName,
|
|
};
|
|
}
|
|
}
|
|
|
|
// ─── Transformer with Custom Hooks ──────────────────────────────
|
|
|
|
class CustomHookTransformer extends TestTransformer {
|
|
override transformGetOneResponse(dto: TestDTO): TestEntity2 {
|
|
const entity = this.transformToEntity(dto);
|
|
return { ...entity, customerName: entity.customerName.toUpperCase() };
|
|
}
|
|
|
|
override transformGetManyResponse(dtos: TestDTO[]): TestEntity2[] {
|
|
return dtos
|
|
.map((dto) => this.transformToEntity(dto))
|
|
.map((entity) => ({ ...entity, bookingCode: `LIST-${entity.bookingCode}` }));
|
|
}
|
|
|
|
override transformCreatePayload(entity: Partial<TestEntity2>): Partial<TestDTO> {
|
|
const dto = super.transformCreatePayload(entity);
|
|
return { ...dto, id: undefined };
|
|
}
|
|
|
|
override transformEditPayload(entity: Partial<TestEntity2>): Partial<TestDTO> {
|
|
const dto = super.transformEditPayload(entity);
|
|
return { ...dto, booking_code: `EDIT-${dto.booking_code}` };
|
|
}
|
|
}
|
|
|
|
// ─── Transformer Integration Tests ──────────────────────────────
|
|
|
|
describe('BaseRemoteDataServices — Data Transformer Integration', () => {
|
|
let mockClient: AxiosInstance;
|
|
|
|
beforeEach(() => {
|
|
mockClient = createMockHttpClient();
|
|
});
|
|
|
|
// ── Without Transformer (backward compatibility) ──────────────
|
|
|
|
describe('without transformer (backward compatibility)', () => {
|
|
it('getOne() returns raw API response unchanged', async () => {
|
|
const rawDTO = { id: '42', booking_code: 'BK042', customer_name: 'Alice' };
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: rawDTO,
|
|
status: 200,
|
|
});
|
|
|
|
const services = new CommonRemoteDataServices(mockClient, {
|
|
apiUrl: '/bookings',
|
|
});
|
|
|
|
const result = await services.getOne('42');
|
|
expect(result.data).toEqual(rawDTO);
|
|
});
|
|
|
|
it('getMany() returns raw API response unchanged', async () => {
|
|
const rawDTOs = [
|
|
{ id: '1', booking_code: 'BK001', customer_name: 'Alice' },
|
|
{ id: '2', booking_code: 'BK002', customer_name: 'Bob' },
|
|
];
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: rawDTOs,
|
|
status: 200,
|
|
});
|
|
|
|
const services = new CommonRemoteDataServices(mockClient, {
|
|
apiUrl: '/bookings',
|
|
});
|
|
|
|
const result = await services.getMany();
|
|
expect(result.data).toEqual(rawDTOs);
|
|
});
|
|
|
|
it('create() sends entity data as-is without transformation', async () => {
|
|
const services = new CommonRemoteDataServices(mockClient, {
|
|
apiUrl: '/bookings',
|
|
});
|
|
|
|
const entityData = { id: 'temp-1' };
|
|
await services.create(entityData);
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data).toEqual(entityData);
|
|
});
|
|
});
|
|
|
|
// ── With Base Transformer (core methods) ──────────────────────
|
|
|
|
describe('with transformer (core transformToEntity / transformToDTO)', () => {
|
|
let services: CommonRemoteDataServices<TestEntity2, TestDTO>;
|
|
const transformer = new TestTransformer();
|
|
|
|
beforeEach(() => {
|
|
services = new CommonRemoteDataServices<TestEntity2, TestDTO>(mockClient, {
|
|
apiUrl: '/bookings',
|
|
moduleKey: 'BOOKING',
|
|
transformer,
|
|
});
|
|
});
|
|
|
|
it('getOne() transforms API DTO to domain entity', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: { id: '42', booking_code: 'BK042', customer_name: 'Alice' },
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getOne('42');
|
|
|
|
expect(result.data).toEqual({
|
|
id: '42',
|
|
bookingCode: 'BK042',
|
|
customerName: 'Alice',
|
|
});
|
|
expect(result.status).toBe(200);
|
|
});
|
|
|
|
it('getMany() transforms each DTO in the array to entities', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: [
|
|
{ id: '1', booking_code: 'BK001', customer_name: 'Alice' },
|
|
{ id: '2', booking_code: 'BK002', customer_name: 'Bob' },
|
|
],
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getMany();
|
|
|
|
expect(result.data).toEqual([
|
|
{ id: '1', bookingCode: 'BK001', customerName: 'Alice' },
|
|
{ id: '2', bookingCode: 'BK002', customerName: 'Bob' },
|
|
]);
|
|
});
|
|
|
|
it('getMany() handles empty array response', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: [],
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getMany();
|
|
|
|
expect(result.data).toEqual([]);
|
|
});
|
|
|
|
it('create() transforms entity payload to DTO before sending', async () => {
|
|
await services.create({
|
|
bookingCode: 'BK001',
|
|
customerName: 'Alice',
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data).toEqual({
|
|
id: undefined,
|
|
booking_code: 'BK001',
|
|
customer_name: 'Alice',
|
|
});
|
|
});
|
|
|
|
it('edit() transforms entity payload to DTO before sending', async () => {
|
|
await services.edit('42', {
|
|
bookingCode: 'BK042-UPDATED',
|
|
customerName: 'Bob',
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data).toEqual({
|
|
id: undefined,
|
|
booking_code: 'BK042-UPDATED',
|
|
customer_name: 'Bob',
|
|
});
|
|
expect(requestArg.url).toBe('/bookings/42');
|
|
});
|
|
|
|
it('delete() is unaffected by transformer (no data transformation needed)', async () => {
|
|
await services.delete('42');
|
|
|
|
expect(mockClient.request).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
url: '/bookings/42',
|
|
method: 'DELETE',
|
|
}),
|
|
);
|
|
});
|
|
|
|
it('customRequest() is unaffected by transformer', async () => {
|
|
await services.customRequest({
|
|
url: '/bookings/42/calculate-tax',
|
|
method: 'POST',
|
|
data: { items: [] },
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data).toEqual({ items: [] });
|
|
});
|
|
});
|
|
|
|
// ── With Custom Hook Transformer ──────────────────────────────
|
|
|
|
describe('with custom operation-specific hooks', () => {
|
|
let services: CommonRemoteDataServices<TestEntity2, TestDTO>;
|
|
const transformer = new CustomHookTransformer();
|
|
|
|
beforeEach(() => {
|
|
services = new CommonRemoteDataServices<TestEntity2, TestDTO>(mockClient, {
|
|
apiUrl: '/bookings',
|
|
moduleKey: 'BOOKING',
|
|
transformer,
|
|
});
|
|
});
|
|
|
|
it('getOne() uses transformGetOneResponse hook (uppercases customer name)', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: { id: '42', booking_code: 'BK042', customer_name: 'alice' },
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getOne('42');
|
|
|
|
expect(result.data).toEqual({
|
|
id: '42',
|
|
bookingCode: 'BK042',
|
|
customerName: 'ALICE', // uppercased by custom hook
|
|
});
|
|
});
|
|
|
|
it('getMany() uses transformGetManyResponse hook (prefixes booking code)', async () => {
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: [{ id: '1', booking_code: 'BK001', customer_name: 'Alice' }],
|
|
status: 200,
|
|
});
|
|
|
|
const result = await services.getMany();
|
|
|
|
expect(result.data).toEqual([{ id: '1', bookingCode: 'LIST-BK001', customerName: 'Alice' }]);
|
|
});
|
|
|
|
it('create() uses transformCreatePayload hook (strips id)', async () => {
|
|
await services.create({
|
|
id: 'should-be-removed',
|
|
bookingCode: 'BK001',
|
|
customerName: 'Alice',
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data.id).toBeUndefined();
|
|
expect(requestArg.data.booking_code).toBe('BK001');
|
|
});
|
|
|
|
it('edit() uses transformEditPayload hook (prefixes booking code)', async () => {
|
|
await services.edit('42', {
|
|
bookingCode: 'BK042',
|
|
customerName: 'Alice',
|
|
});
|
|
|
|
const requestArg = (mockClient.request as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
expect(requestArg.data.booking_code).toBe('EDIT-BK042');
|
|
});
|
|
});
|
|
|
|
// ── Identity Transformer ──────────────────────────────────────
|
|
|
|
describe('with identity transformer (default passthrough)', () => {
|
|
it('produces same results as no transformer', async () => {
|
|
const rawData = { id: '1', name: 'Test' };
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
data: rawData,
|
|
status: 200,
|
|
});
|
|
|
|
// Service without transformer
|
|
const servicesNoTransformer = new CommonRemoteDataServices(mockClient, {
|
|
apiUrl: '/items',
|
|
});
|
|
|
|
// Service with identity transformer (no method overrides)
|
|
class IdentityTransformer extends BaseDataTransformer {}
|
|
const servicesWithIdentity = new CommonRemoteDataServices(mockClient, {
|
|
apiUrl: '/items',
|
|
transformer: new IdentityTransformer(),
|
|
});
|
|
|
|
const resultWithout = await servicesNoTransformer.getOne('1');
|
|
const resultWith = await servicesWithIdentity.getOne('1');
|
|
|
|
expect(resultWithout.data).toEqual(resultWith.data);
|
|
});
|
|
});
|
|
|
|
// ── Transformer receives correct arguments ────────────────────
|
|
|
|
describe('transformer method invocation', () => {
|
|
it('transformGetOneResponse receives the raw DTO from API', async () => {
|
|
const rawDTO = { id: '42', booking_code: 'BK042', customer_name: 'Alice' };
|
|
(mockClient.request as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
data: rawDTO,
|
|
status: 200,
|
|
});
|
|
|
|
const mockTransformer: IDataTransformer<TestEntity2, TestDTO> = {
|
|
transformToEntity: vi.fn((dto: TestDTO) => ({
|
|
id: dto.id,
|
|
bookingCode: dto.booking_code,
|
|
customerName: dto.customer_name,
|
|
})),
|
|
transformToDTO: vi.fn(),
|
|
transformGetOneResponse: vi.fn((dto: TestDTO) => ({
|
|
id: dto.id,
|
|
bookingCode: dto.booking_code,
|
|
customerName: dto.customer_name,
|
|
})),
|
|
transformGetManyResponse: vi.fn(),
|
|
transformCreatePayload: vi.fn(),
|
|
transformEditPayload: vi.fn(),
|
|
};
|
|
|
|
const services = new CommonRemoteDataServices<TestEntity2, TestDTO>(mockClient, {
|
|
apiUrl: '/bookings',
|
|
transformer: mockTransformer,
|
|
});
|
|
|
|
await services.getOne('42');
|
|
|
|
expect(mockTransformer.transformGetOneResponse).toHaveBeenCalledWith(rawDTO);
|
|
expect(mockTransformer.transformToEntity).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('transformCreatePayload receives the entity data from caller', async () => {
|
|
const entityData: Partial<TestEntity2> = {
|
|
bookingCode: 'BK001',
|
|
customerName: 'Alice',
|
|
};
|
|
|
|
const mockTransformer: IDataTransformer<TestEntity2, TestDTO> = {
|
|
transformToEntity: vi.fn(),
|
|
transformToDTO: vi.fn(),
|
|
transformGetOneResponse: vi.fn(),
|
|
transformGetManyResponse: vi.fn(),
|
|
transformCreatePayload: vi.fn((entity) => ({
|
|
booking_code: entity.bookingCode!,
|
|
customer_name: entity.customerName!,
|
|
})),
|
|
transformEditPayload: vi.fn(),
|
|
};
|
|
|
|
const services = new CommonRemoteDataServices<TestEntity2, TestDTO>(mockClient, {
|
|
apiUrl: '/bookings',
|
|
transformer: mockTransformer,
|
|
});
|
|
|
|
await services.create(entityData);
|
|
|
|
expect(mockTransformer.transformCreatePayload).toHaveBeenCalledWith(entityData);
|
|
expect(mockTransformer.transformToDTO).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|