import { SpyBuilder, createAxiosResponse, createAxiosError } from '../spy-builder'; // Create a concrete implementation of SpyBuilder for testing class TestSpyBuilder extends SpyBuilder {} describe('SpyBuilder', () => { let spyBuilder: TestSpyBuilder; beforeEach(() => { spyBuilder = new TestSpyBuilder(); jest.clearAllMocks(); }); describe('createAxiosResponse', () => { it('should create default AxiosResponse with provided data', () => { const testData = { message: 'test' }; const response = createAxiosResponse({ data: testData }); expect(response).toEqual({ data: testData, status: 200, statusText: 'OK', headers: {}, config: {}, }); }); it('should create AxiosResponse with custom options', () => { const testData = { error: 'not found' }; const response = createAxiosResponse({ data: testData, status: 404, statusText: 'Not Found', headers: { 'content-type': 'application/json' }, }); expect(response).toEqual({ data: testData, status: 404, statusText: 'Not Found', headers: { 'content-type': 'application/json' }, config: {}, }); }); it('should create AxiosResponse with undefined data when no data provided', () => { const response = createAxiosResponse({}); expect(response.data).toBeUndefined(); expect(response.status).toBe(200); }); }); describe('createAxiosError', () => { it('should create AxiosError with default response', () => { const error = createAxiosError({}); expect(error).toMatchObject({ name: 'AxiosError', message: 'Request failed with status code 400', isAxiosError: true, config: {}, }); expect(error.response).toEqual({ data: undefined, status: 200, statusText: 'OK', headers: {}, config: {}, }); }); it('should create AxiosError with custom response options', () => { const errorData = { error: 'Invalid request' }; const error = createAxiosError({ data: errorData, status: 400, statusText: 'Bad Request', }); expect(error.response).toEqual({ data: errorData, status: 400, statusText: 'Bad Request', headers: {}, config: {}, }); }); it('should have toJSON method', () => { const error = createAxiosError({}); const json = error.toJSON(); expect(json).toEqual({ name: 'AxiosError', message: 'Request failed with status code 400', }); }); }); describe('SpyBuilder', () => { describe('add', () => { it('should add a response with data and return spy', () => { const mockSpy = jest.fn(); const testData = { id: 1, name: 'test' }; spyBuilder.add({ key: 'test-key', data: testData, spy: mockSpy, }); const retrievedSpy = spyBuilder.getSpy('test-key'); expect(retrievedSpy).toBe(mockSpy); expect(mockSpy).toHaveBeenCalledTimes(0); // Implementation should be set but not called }); it('should return undefined spy when no spy provided', () => { spyBuilder.add({ key: 'test-key', data: { message: 'test' }, }); const retrievedSpy = spyBuilder.getSpy('test-key'); expect(retrievedSpy).toBeUndefined(); }); it('should set up successful response implementation', async () => { const mockSpy = jest.fn(); const testData = { success: true }; spyBuilder.add({ key: 'success-key', data: testData, spy: mockSpy, }); // Call the mocked function to test implementation const result = await mockSpy(); expect(result).toEqual({ data: testData, status: 200, statusText: 'OK', headers: {}, config: {}, }); }); it('should set up rejection implementation when reject is true', async () => { const mockSpy = jest.fn(); spyBuilder.add({ key: 'error-key', spy: mockSpy, reject: true, }); await expect(mockSpy()).rejects.toMatchObject({ name: 'AxiosError', message: 'Request failed with status code 400', isAxiosError: true, }); }); it('should set up rejection with custom error options', async () => { const mockSpy = jest.fn(); spyBuilder.add({ key: 'custom-error-key', spy: mockSpy, reject: true, options: { status: 500, statusText: 'Internal Server Error', }, }); await expect(mockSpy()).rejects.toMatchObject({ response: expect.objectContaining({ status: 500, statusText: 'Internal Server Error', data: { Error: { Message: 'Bad Request', }, }, }), }); }); it('should use custom impersonate function', async () => { const mockSpy = jest.fn(); const customResponse = createAxiosResponse({ data: { custom: 'response' } }); const impersonateFunc = jest.fn(() => Promise.resolve(customResponse)); spyBuilder.add({ key: 'impersonate-key', spy: mockSpy, impersonate: impersonateFunc, }); const result = await mockSpy('arg1', 'arg2'); expect(result).toBe(customResponse); expect(impersonateFunc).toHaveBeenCalledWith('arg1', 'arg2'); }); it('should merge custom options with data', async () => { const mockSpy = jest.fn(); const testData = { message: 'test' }; spyBuilder.add({ key: 'options-key', data: testData, spy: mockSpy, options: { status: 201, statusText: 'Created', headers: { 'x-custom': 'header' }, }, }); const result = await mockSpy(); expect(result).toEqual({ data: testData, status: 201, statusText: 'Created', headers: { 'x-custom': 'header' }, config: {}, }); }); it('should not overwrite existing response for same key', () => { const firstSpy = jest.fn(); const secondSpy = jest.fn(); spyBuilder.add({ key: 'duplicate-key', spy: firstSpy, data: { first: true }, }); spyBuilder.add({ key: 'duplicate-key', spy: secondSpy, data: { second: true }, }); const retrievedSpy = spyBuilder.getSpy('duplicate-key'); expect(retrievedSpy).toBe(firstSpy); }); it('should handle empty key', () => { const mockSpy = jest.fn(); spyBuilder.add({ spy: mockSpy, data: { test: true }, }); const retrievedSpy = spyBuilder.getSpy(''); expect(retrievedSpy).toBe(mockSpy); }); it('should return builder instance for method chaining', () => { const result = spyBuilder.add({ key: 'chain-key', data: { test: true }, }); expect(result).toBe(spyBuilder); }); }); describe('getSpy', () => { it('should return undefined for non-existent key', () => { const spy = spyBuilder.getSpy('non-existent'); expect(spy).toBeUndefined(); }); it('should return correct spy for existing key', () => { const mockSpy = jest.fn(); spyBuilder.add({ key: 'existing-key', spy: mockSpy, }); const retrievedSpy = spyBuilder.getSpy('existing-key'); expect(retrievedSpy).toBe(mockSpy); }); }); }); });