diff --git a/__teremocks__/some_name/get-q-click.json b/__teremocks__/some_name/get-q-click.json new file mode 100644 index 0000000..abb9a6b --- /dev/null +++ b/__teremocks__/some_name/get-q-click.json @@ -0,0 +1,24 @@ +{ + "request": { + "url": "http://localhost:3000/api?q=click", + "method": "GET", + "headers": { + "referer": "http://localhost:3000/" + }, + "body": null, + "resourceType": "fetch" + }, + "response": { + "url": "http://localhost:3000/api?q=click", + "status": 200, + "headers": { + "content-type": "application/json; charset=utf-8", + "access-control-allow-origin": "*", + "connection": "keep-alive", + "keep-alive": "timeout=5" + }, + "body": { + "suggest": "click" + } + } +} diff --git a/__tests__/generator.pw.ts b/__tests__/generator.pw.ts index fe53d80..ad9c736 100644 --- a/__tests__/generator.pw.ts +++ b/__tests__/generator.pw.ts @@ -3,7 +3,7 @@ import path from 'path' import { expect, test as base } from '@playwright/test' import rimraf from 'rimraf' import sinon from 'sinon' -import { Teremock, parseUrl } from '../index' +import { parseUrl, Teremock, UserInterceptor } from '../index' import type { Request } from '../src/types' @@ -14,7 +14,7 @@ async function sleep(time: number): Promise { } const test = base.extend<{ teremock: Teremock }, { cleanup: void }>({ - teremock: async ({ page }, use) => { + teremock: async ({ page: _page }, use) => { const teremock = new Teremock() await use(teremock) await teremock.stop() @@ -236,6 +236,56 @@ test.describe('teremock puppeteer', async () => { await teremock.stop() }) + test('capture GET request for /api and extend response', async ({ page }) => { + await page.goto('http://localhost:3000') + + const interceptors: Record = { + some_name: { + methods: new Set(['*']), + resourceTypes: new Set(['*']), + pass: false, + url: '/api', + response: async (_, res) => { + if (!res) { + return {} + } + + return { + body: { + ...res.body, + suggest: [res.body?.suggest, 'one more suggest'], + }, + } + }, + }, + } + + // * Starting mocker with wildcard interceptor + // @ts-ignore + const teremock = new Teremock() + await teremock.start({ page, interceptors, ci: false }) + + // * Invoking GET request to `/api` + await page.click('#button') + await sleep(100) + + const text = await page.evaluate((element) => element?.textContent, await page.$('#button')) + + // before mock we can't modify response. Check base response + expect(text).toBe('200 click') + + // * Invoking GET request to `/api` + await page.click('#button') + await sleep(100) + + + const textAfterMock = await page.evaluate((element) => element?.textContent, await page.$('#button')) + + expect(textAfterMock).toBe('200 click,one more suggest') + + await teremock.stop() + }) + test('dont capture GET request for /api when methods are ["post"]', async ({ page, teremock }) => { await page.goto('http://localhost:3000') diff --git a/package.json b/package.json index 5613c88..935e5b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "teremock", - "version": "2.0.8", + "version": "2.1.0", "description": "File-based real mocks request mocker for playwright", "repository": { "type": "git", diff --git a/src/handleRequest.ts b/src/handleRequest.ts index f4bf2f7..e28dbdb 100644 --- a/src/handleRequest.ts +++ b/src/handleRequest.ts @@ -25,6 +25,7 @@ export interface BeforeRespondArg { respond: (response: Response, interceptor: Interceptor) => Promise request: Request response: DefResponse + mockedResponse?: Response responseOverrides?: Partial interceptor: Interceptor mog: Function @@ -35,6 +36,7 @@ async function beforeRespond({ respond, request, response, + mockedResponse, mog, interceptor, increment, @@ -48,7 +50,7 @@ async function beforeRespond({ query: getQuery(request.url), } mog(`» response is a function, responding with its returns`) - partResp = await response(argRequest) + partResp = await response(argRequest, mockedResponse) mog(`» response() returns`, partResp) } else { partResp = response @@ -130,13 +132,21 @@ export default function createHandler(initialParams: Params) { reqSet.add(mockId) mog('» reqSet is', Array.from(reqSet.get())) - if (interceptor.response) { - loggerTrace(`${request.url} ← inline response`) - mog(`» interceptor.response defined, responding with it`) + // mocks from storage + mog(`» trying to get mock with id "${mockId}"`) + + if (await storage.has(mockId)) { + mog(`» mock "${mockId}" exists!`) + + const mock = await storage.get(mockId) + + loggerTrace(`${request.url} ← mock ${mockId} found in storage`) + mog(`» successfully read from "${mockId}", responding`) await beforeRespond({ request, - response: interceptor.response, + response: interceptor.response || mock.response, + mockedResponse: mock.response, responseOverrides, respond, interceptor, @@ -147,37 +157,32 @@ export default function createHandler(initialParams: Params) { return } - // mocks from storage - - mog(`» trying to get mock with id "${mockId}"`) - - if (await storage.has(mockId)) { - mog(`» mock "${mockId}" exists!`) - - const mock = await storage.get(mockId) - - loggerTrace(`${request.url} ← mock ${mockId} found in storage`) - mog(`» successfully read from "${mockId}", responding`) + const needMockInResponseFn = typeof interceptor.response === 'function' && interceptor.response.length === 2 + if (interceptor.response && !needMockInResponseFn) { + loggerTrace(`${request.url} ← inline response`) + mog(`» interceptor.response defined, responding with it`) await beforeRespond({ request, - response: mock.response, + response: interceptor.response, responseOverrides, respond, interceptor, mog, increment, }) + + return + } + + loggerTrace(`${request.url} ← mock not found in storage`) + mog(`» mock does not exist!`, ci) + + if (ci) { + signale.warn(`mock file not found in ci mode, url is "${request.url}"`) } else { - loggerTrace(`${request.url} ← mock not found in storage`) - mog(`» mock does not exist!`, ci) - - if (ci) { - signale.warn(`mock file not found in ci mode, url is "${request.url}"`) - } else { - mog('» about to next()...') - await next(interceptor) - } + mog('» about to next()...') + await next(interceptor) } } } diff --git a/src/handleResponse.ts b/src/handleResponse.ts index c7406f1..5f6be63 100644 --- a/src/handleResponse.ts +++ b/src/handleResponse.ts @@ -47,6 +47,8 @@ export default function createHandler(initialParams: Params) { const mockId = getMockId({ ...request, naming, name: interceptor.name, body: getBody(request.body) }) const mockExist: boolean = await storage.has(mockId) const hasResp = !!interceptor.response + const needMockInResponseFn = + hasResp && typeof interceptor.response === 'function' && interceptor.response.length === 2 const mog = debug(`teremock:${mockId}`) /** @@ -58,7 +60,7 @@ export default function createHandler(initialParams: Params) { } else if (mockExist) { loggerTrace(`${request.url} → mock already exists`) mog(`« mock was not stored because it exists`) - } else if (hasResp) { + } else if (hasResp && !needMockInResponseFn) { loggerTrace(`${request.url} → interceptor.response is defined`) mog(`« mock was not stored because matched interceptor have response property`) } else if (interceptor.pass) { diff --git a/src/index.ts b/src/index.ts index ae768e0..809bb2b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,3 +8,5 @@ export default teremock export * from './types' export * from './utils' +export { default as getMockId } from './mock-id' +export { default as getRequestId } from './request-id' diff --git a/src/types.ts b/src/types.ts index e973f4b..ee242f9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -39,7 +39,7 @@ export interface GetMockIdParams { headers?: Headers } -export type ResponseFunc = (req: ArgRequest) => Promise> +export type ResponseFunc = (req: ArgRequest, mockedResponse?: Response) => Promise> export type DefResponse = Partial | ResponseFunc type ListItem = string | string[]