import { Page } from 'playwright'; import { AxiosResponse, AxiosInstance } from 'axios'; import fs from 'fs'; import path from 'path'; interface MockResponse { status: number; body: any; headers?: { [key: string]: string }; } class MockApiManager { private static instance: MockApiManager; private mockResponses: { [url: string]: MockResponse } = {}; private constructor() { } public static referralNumber: string; public static referralSequenceNumber: string; public static referralCorrelationId: string; public static savedSessionId: string; public static getInstance(): MockApiManager { if (!MockApiManager.instance) { MockApiManager.instance = new MockApiManager(); } return MockApiManager.instance; } /** * Loads mock responses from a configuration file. * @param scenario - The scenario to load mock responses for. */ private loadMockResponses(scenario: string): void { const mockResponsesDir = path.resolve(__dirname, '../../tests/mockResponses'); const configFilePath = path.join(mockResponsesDir, 'mockResponsesConfig.json'); if (fs.existsSync(configFilePath)) { const config = JSON.parse(fs.readFileSync(configFilePath, 'utf-8')); const scenarioConfig = config[scenario]; const commonConfig = config['common']; for (const endpoint in scenarioConfig) { this.loadMockResponse(endpoint, scenarioConfig[endpoint], mockResponsesDir); } for (const endpoint in commonConfig) { if (!this.mockResponses[endpoint]) { this.loadMockResponse(endpoint, commonConfig[endpoint], mockResponsesDir); } } } else { console.error(`Mock responses configuration file not found at path: ${configFilePath}`); } } /** * Loads a single mock response from a file. * @param endpoint - The endpoint to load the mock response for. * @param mockFilePath - The path to the mock response file. * @param mockResponsesDir - The directory containing the mock response files. */ private loadMockResponse(endpoint: string, mockFilePath: string, mockResponsesDir: string): void { const filePath = path.join(mockResponsesDir, mockFilePath); if (fs.existsSync(filePath)) { const mockResponse = fs.readFileSync(filePath, 'utf-8'); try { this.mockResponses[endpoint] = JSON.parse(mockResponse); } catch (error) { console.error(`Error parsing mock response for endpoint: ${endpoint}`, error); } } else { console.warn(`Mock response file${filePath} not found for endpoint: ${endpoint}`); } } /** * Gets the mocked API response for the given endpoint. * @param endpoint - The endpoint to get the mocked response for. * @param scenario - The scenario to load mock responses for. * @returns The mocked response or null if not found. */ public getMockedApiResponse(endpoint: string, scenario: string): MockResponse | null { if (!this.mockResponses[endpoint]) { this.loadMockResponses(scenario); } return this.mockResponses[endpoint] || null; } /** * Mocks the API response for the given endpoint. * @param page - The Playwright page object. * @param endpoint - The endpoint to mock. * @param scenario - The scenario to load mock responses for. * @param mockTestingFlag - Flag to enable or disable mocking. */ public async mockApiResponse(page: Page, scenario: string, mockTestingFlag: boolean): Promise { if (!mockTestingFlag) { console.info("mockTestingFlag is false/undefined. so proceeding with original requests.") return; } if (!scenario) { console.error('Invalid scenario provided for mocking.'); return; } this.loadMockResponses(scenario); for (const endpoint in this.mockResponses) { await page.route(`**/${endpoint}`, async (route) => { const response = this.mockResponses[endpoint]; if (response) { route.fulfill({ status: response.status || 200, body: JSON.stringify(response.body || response), headers: response.headers || { 'Content-Type': 'application/json' }, }); } console.info("Mocking Endpoint: " + endpoint) }); } } public async updateReferralDetails(endpoint: string, response: MockResponse): Promise { try { response.body.referralCorrelationId = MockApiManager.referralCorrelationId; response.body.referralNumber = MockApiManager.referralNumber; } catch (error) { } return response; } public async getReferralDetails(endpoint: string, response: MockResponse) { if (endpoint.includes('order/save-session/iss')) { MockApiManager.referralNumber = response.body.referralNumber; MockApiManager.referralSequenceNumber = response.body.referralSequenceNumber; MockApiManager.referralCorrelationId = response.body.referralCorrelationId; MockApiManager.savedSessionId = response.body.sessionId; } } } // Export the instance export const mockApiManager = MockApiManager.getInstance(); export async function httpGet(client: AxiosInstance, url: string): Promise { const [isSuccess, response] = await handleHttp(client.get(url)); if (isSuccess) { return response; } console.error(`An error occurred calling GET ${url}\nError:${response}`); throw response; } export async function httpPost(client: AxiosInstance, url: string, data: D): Promise { const [isSuccess, response] = await handleHttp(client.post(url, data)); if (isSuccess) { return response; } console.error(`An error occurred calling POST ${url}\nError:${response}`); throw response; } export function handleHttp(request: Promise>): Promise<[isSuccess: true, data: T] | [isSuccess: false, error: Error]> { return request.then(data => { return [true, data.data] as [true, T] }).catch((error: Error) => { return [false, error] as [false, Error] }) } export function buildQueryString(data: T): URLSearchParams { const params: Record = {}; for (const key in data) { const value = data[key]; params[key] = `${value}`; } return new URLSearchParams(params); } export function forceAPIError(page: Page, endpoint: string) { page.route('**/*', (route) => { return route.request().url().includes(endpoint) ? route.abort() : route.continue() }); }