playwright#Request TypeScript Examples

The following examples show how to use playwright#Request. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: record-requests-to.common.test.ts    From playwright-fluent with MIT License 6 votes vote down vote up
describe('record requests to', (): void => {
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});

  test('should return an error when browser has not been launched', async (): Promise<void> => {
    // Given
    const page: Page | undefined = undefined;
    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    const takeAllPredicate = () => false;

    // When
    // Then
    const expectedError = new Error(
      "Cannot record requests to '/foobar' because no browser has been launched",
    );
    await SUT.recordRequestsTo('/foobar', takeAllPredicate, page, callback).catch((error): void =>
      expect(error).toMatchObject(expectedError),
    );
  });
});
Example #2
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 6 votes vote down vote up
/**
   * Will track and record requests whose url contains the input url.
   * Usefull when you need to check what the front sends to the back and/or what the back sends to the front.
   * Each recorded request is a standard `playwright` request object that contains both the request and the response.
   *
   * @param {string} partialUrl This parameter should be seen as a partial url (it is not a regex and not a glob pattern).
   * @param {(request: Request) => boolean} [ignorePredicate=() => false] optional predicate to provide if you want to ignore specific requests
   * @returns {PlaywrightFluent}
   * @memberof PlaywrightFluent
   * @example
   *  const p = new PlaywrightFluent();
   *  await p
   *    .withBrowser('chromium')
   *    .withOptions({ headless: true })
   *    .withCursor()
   *    .recordRequestsTo('/api/v1/user', (request) => request.method() === 'OPTIONS')
   *    .recordRequestsTo('/api/v1/books')
   *    .navigateTo(url);
   */
  public recordRequestsTo(
    partialUrl: string,
    ignorePredicate: (request: Request) => boolean = () => false,
  ): PlaywrightFluent {
    const action = (): Promise<void> => this.recordRequestsToUrl(partialUrl, ignorePredicate);
    this.actions.push(action);
    return this;
  }
Example #3
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 6 votes vote down vote up
public onRequestTo(
    partialUrl: string,
    options: Partial<RequestInterceptionFilterOptions> = {},
  ): {
    respondWith: <T>(
      response: Partial<MockedResponse<T>> | ((request: Request) => Partial<MockedResponse<T>>),
    ) => PlaywrightFluent;
    respondFromHar: (
      harFiles: string[],
      options?: Partial<HarRequestResponseOptions>,
    ) => PlaywrightFluent;
  } {
    return {
      respondWith: <T>(
        response: Partial<MockedResponse<T>> | ((request: Request) => Partial<MockedResponse<T>>),
      ): PlaywrightFluent => {
        const action = (): Promise<void> =>
          this.onRequestToRespondWith(partialUrl, options, response);
        this.actions.push(action);
        return this;
      },
      respondFromHar: (
        harFiles: string[],
        options?: Partial<HarRequestResponseOptions>,
      ): PlaywrightFluent => {
        const harOptions: HarRequestResponseOptions = {
          ...defaultHarRequestResponseOptions,
          ...options,
        };
        const action = (): Promise<void> =>
          this.onRequestToRespondFromHar(partialUrl, harFiles, harOptions);
        this.actions.push(action);
        return this;
      },
    };
  }
Example #4
Source File: request.mock.ts    From playwright-fluent with MIT License 6 votes vote down vote up
mockRequest: Request = {
  url: jest.fn(),
  method: jest.fn(),
  failure: jest.fn(),
  frame: jest.fn(),
  headers: jest.fn(),
  postData: jest.fn(),
  isNavigationRequest: jest.fn(),
  postDataBuffer: jest.fn(),
  redirectedFrom: jest.fn(),
  postDataJSON: jest.fn(),
  redirectedTo: jest.fn(),
  resourceType: jest.fn(),
  timing: jest.fn(),
  response: jest.fn(),
  allHeaders: jest.fn(),
  headersArray: jest.fn(),
  headerValue: jest.fn(),
  sizes: jest.fn(),
}
Example #5
Source File: record-requests-to.ts    From playwright-fluent with MIT License 6 votes vote down vote up
export async function recordRequestsTo(
  partialUrl: string,
  ignorePredicate: (request: Request) => boolean,
  page: Page | undefined,
  callback: (request: Request) => void,
): Promise<void> {
  if (!page) {
    throw new Error(
      `Cannot record requests to '${partialUrl}' because no browser has been launched`,
    );
  }

  page.on('requestfinished', (request) => {
    const requestedUrl = request.url();
    const shouldIgnoreRequest = ignorePredicate(request);
    if (shouldIgnoreRequest) {
      return;
    }
    if (requestedUrl && requestedUrl.includes(partialUrl)) {
      callback(request);
      return;
    }
  });

  page.on('requestfailed', (request) => {
    const requestedUrl = request.url();
    const shouldIgnoreRequest = ignorePredicate(request);
    if (shouldIgnoreRequest) {
      return;
    }
    if (requestedUrl && requestedUrl.includes(partialUrl)) {
      callback(request);
      return;
    }
  });
}
Example #6
Source File: stringify-request.ts    From playwright-fluent with MIT License 6 votes vote down vote up
export async function toRequestInfo(request: Request): Promise<RequestInfo> {
  const requestInfo: RequestInfo = {
    url: request.url(),
    queryString: toQueryString(request.url()),
    method: request.method(),
    error: request.failure(),
    headers: request.headers(),
    postData: toJsonOrDefault(request.postData()),
    response: null,
  };

  const response = await request.response();
  if (response === null) {
    return requestInfo;
  }

  let responseBody: string | unknown | undefined = undefined;
  try {
    responseBody = await toJsonOrText(response);
  } catch (error) {
    // ignore error
  }

  const responseInfo: ResponseInfo = {
    headers: response.headers(),
    payload: responseBody,
    status: response.status(),
    statusText: response.statusText(),
  };

  requestInfo.response = responseInfo;
  return requestInfo;
}
Example #7
Source File: record-failed-requests-to.common.test.ts    From playwright-fluent with MIT License 6 votes vote down vote up
describe('record failed requests', (): void => {
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});

  test('should return an error when browser has not been launched', async (): Promise<void> => {
    // Given
    const page: Page | undefined = undefined;
    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);

    // When
    // Then
    const expectedError = new Error(
      'Cannot record failed requests because no browser has been launched',
    );
    await SUT.recordFailedRequests(page, callback).catch((error): void =>
      expect(error).toMatchObject(expectedError),
    );
  });
});
Example #8
Source File: record-failed-requests.ts    From playwright-fluent with MIT License 6 votes vote down vote up
export async function recordFailedRequests(
  page: Page | undefined,
  callback: (request: Request) => void,
): Promise<void> {
  if (!page) {
    throw new Error(`Cannot record failed requests because no browser has been launched`);
  }

  page.on('requestfinished', async (request) => {
    const response = await request.response();
    if (response === null) {
      const typedRequest = request as unknown as Request;
      callback(typedRequest);
      return;
    }

    const status = response.status();
    if (failedStatus.includes(status)) {
      const typedRequest = request as unknown as Request;
      callback(typedRequest);
      return;
    }
  });

  page.on('requestfailed', (request) => {
    const typedRequest = request as unknown as Request;
    callback(typedRequest);
    return;
  });
}
Example #9
Source File: on-request-to-respond-with.ts    From playwright-fluent with MIT License 6 votes vote down vote up
export async function onRequestToRespondWith<T>(
  url: string,
  options: Partial<RequestInterceptionFilterOptions>,
  response: Partial<MockedResponse<T>> | ((request: Request) => Partial<MockedResponse<T>>),
  page: Page | undefined,
): Promise<void> {
  if (!page) {
    throw new Error(`Cannot intercept requests to '${url}' because no browser has been launched`);
  }

  await page.route(
    (uri) => {
      return uri.toString().includes(url);
    },
    (route, request) => {
      const requestMethod = request.method();
      if (options && typeof options.method === 'string' && options.method !== requestMethod) {
        route.continue();
        return;
      }

      if (
        options &&
        typeof options.bypassPredicate === 'function' &&
        options.bypassPredicate(request)
      ) {
        route.continue();
        return;
      }

      const mockedResponse = typeof response === 'function' ? response(request) : response;
      const playwrightResponse = buildPlaywrightResponseWith(mockedResponse);
      route.fulfill(playwrightResponse);
    },
  );
}
Example #10
Source File: stringify-request.ts    From playwright-fluent with MIT License 5 votes vote down vote up
export async function stringifyRequest(request: Request): Promise<string> {
  const requestInfo = await toRequestInfo(request);
  const result = JSON.stringify(requestInfo, null, 2);

  return result;
}
Example #11
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
public getLastRecordedRequestTo(url: string): Request | undefined {
    return this.sentRequests.filter((req) => req.url().includes(url)).pop();
  }
Example #12
Source File: har-reader.test.ts    From playwright-fluent with MIT License 5 votes vote down vote up
describe('har-reader', (): void => {
  test('should find response for an http GET', async (): Promise<void> => {
    // Given
    const harFile = path.join(__dirname, 'har-get.har');
    const harData = getHarDataFrom(harFile);
    const request: Request = {
      ...mockRequest,
      url: () => 'http://localhost:8080/foobar?foo=bar',
      method: () => 'GET',
    };

    // When
    const result = SUT.getHarResponseFor(request, harData);

    // Then
    expect(result).toBeDefined();
  });

  test('should find response for an http POST', async (): Promise<void> => {
    // Given
    const harFile = path.join(__dirname, 'har-post.har');
    const harData = getHarDataFrom(harFile);
    const request: Request = {
      ...mockRequest,
      url: () => 'http://localhost:8080/foobar?foo=bar',
      method: () => 'POST',
      postData: () => '{"foo":"bar"}',
    };

    // When
    const result = SUT.getHarResponseFor(request, harData);

    // Then
    expect(result).toBeDefined();
  });

  test('should not find response for an http POST with unfound postdata', async (): Promise<void> => {
    // Given
    const harFile = path.join(__dirname, 'har-post.har');
    const harData = getHarDataFrom(harFile);
    const request: Request = {
      ...mockRequest,
      url: () => 'http://localhost:8080/foobar?foo=bar',
      method: () => 'POST',
      postData: () => '{"foo":"baz"}',
    };

    // When
    const result = SUT.getHarResponseFor(request, harData);

    // Then
    expect(result).toBeUndefined();
  });
});
Example #13
Source File: har-reader.ts    From playwright-fluent with MIT License 5 votes vote down vote up
export function getHarResponseFor(
  request: Request,
  harData: HarData,
  options: Partial<HarEntryParserOptions> = defaultHarEntryParserOptions,
): HarResponse | undefined {
  const harEntry = getHarEntryFor(request, harData, options);
  return harEntry?.response;
}
Example #14
Source File: har-reader.ts    From playwright-fluent with MIT License 5 votes vote down vote up
export function getHarEntryFor(
  request: Request,
  harData: HarData,
  options: Partial<HarEntryParserOptions> = defaultHarEntryParserOptions,
): HarEntry | undefined {
  const entryOptions = {
    ...defaultHarEntryParserOptions,
    ...options,
  };

  const httpMethod = request.method() as HttpRequestMethod;
  const url = request.url();

  const entries = harData.log.entries
    .filter((entry) => entry.request.method === httpMethod)
    .filter((entry) => entryOptions.areUrlEquals(entry.request.url, url))
    .filter((entry) => entry.response)
    .filter((entry) => entry.response.status >= 200 && entry.response.status < 300);

  const entriesWithSamePostData = entries.filter(
    (entry) =>
      entry.request.postData?.text === request.postData() ||
      (request.postData() === null && entry.request.postData?.text === undefined),
  );

  switch (httpMethod) {
    case 'CONNECT':
    case 'GET':
    case 'HEAD':
    case 'OPTIONS':
    case 'TRACE':
      return entries.pop();

    case 'DELETE':
    case 'PATCH':
    case 'POST':
    case 'PUT':
      return entriesWithSamePostData.pop();

    default:
      throw new Error(`HAR-READER: the HTTP Method ${httpMethod} is not yet implemented`);
  }
}
Example #15
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
private failedRequests: Request[] = [];
Example #16
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
private sentRequests: Request[] = [];
Example #17
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
public getFailedRequests(): Request[] {
    return [...this.failedRequests];
  }
Example #18
Source File: common.ts    From jitsu with MIT License 5 votes vote down vote up
/**
 * Opens an url with given browser:
 * - throws an exception (fails the test) if result is not successful
 * - takes a screenshot and saves it to `artifacts`
 * @param browser
 * @param url
 */
export async function runUrl(
  browser: Browser,
  url: string
): Promise<PageResult> {
  let allRequests: Request[] = [];
  let consoleErrors: string[] = [];
  const page = await browser.newPage();
  page.on("request", (request) => {
    console.log("Page requested " + request.url());
    allRequests.push(request);
  });
  page.on("console", (msg: ConsoleMessage) => {
    if (msg.type() === "error") {
      consoleErrors.push(msg.text());
    }
    console.log(`Browser console message: [${msg.type()}] ${msg.text()}`);
  });
  page.on("pageerror", (err) => {
    consoleErrors.push(err.message);
    console.log(`Browser console error: ${err.message}`);
  });
  let artifactsDir = __dirname + "/../artifacts/";
  let screenshotPath =
    artifactsDir +
    url.replace("http://", "").replace("https://", "").split("/").join("_") +
    ".png";
  let result = await page.goto(url);
  console.log(`Saving screenshot of ${url} to ${screenshotPath}`);
  await page.screenshot({ path: screenshotPath });

  expect(result?.ok()).toBeTruthy();

  console.log("Waiting");
  await sleep(4000);

  await page.close();
  return { allRequests, consoleErrors, pageResponse: result as Response };
}
Example #19
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
private async onRequestToRespondWith<T>(
    partialUrl: string,
    options: Partial<RequestInterceptionFilterOptions>,
    response: Partial<MockedResponse<T>> | ((request: Request) => Partial<MockedResponse<T>>),
  ): Promise<void> {
    await action.onRequestToRespondWith(partialUrl, options, response, this.currentPage());
  }
Example #20
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
private async recordRequestsToUrl(
    partialUrl: string,
    ignorePredicate: (request: Request) => boolean,
  ): Promise<void> {
    await action.recordRequestsTo(partialUrl, ignorePredicate, this.currentPage(), (request) =>
      this.sentRequests.push(request),
    );
  }
Example #21
Source File: playwright-fluent.ts    From playwright-fluent with MIT License 5 votes vote down vote up
public getRecordedRequestsTo(url: string): Request[] {
    return [...this.sentRequests.filter((req) => req.url().includes(url))];
  }
Example #22
Source File: with-mocks.ts    From playwright-fluent with MIT License 5 votes vote down vote up
export function getPostDataOf(request: Request): PostData {
  try {
    return request.postDataJSON();
  } catch (error) {
    return request.postData();
  }
}
Example #23
Source File: record-failed-requests.chromium.test.ts    From playwright-fluent with MIT License 5 votes vote down vote up
describe('record failed requests', (): void => {
  let browser: Browser | undefined = undefined;
  let fakeServer: FakeServer | undefined = undefined;
  beforeAll(() => {
    fakeServer = new FakeServer(1237);
    fakeServer.start();
    //The FakeServer now listens on http://localhost:1237
  });
  afterAll(() => {
    if (fakeServer) {
      fakeServer.stop();
    }
  });
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});
  afterEach(async (): Promise<void> => {
    if (browser) {
      await browser.close();
    }
  });

  test('should record HTP 500 requests', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/500')
        .willFail(500);

    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);

    // When
    await SUT.recordFailedRequests(page, callback);
    await page.goto(`file:${path.join(__dirname, 'record-failed-requests-500.test.html')}`);
    await sleep(3000);

    // Then
    expect(requests.length).toBe(1);

    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(500);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.statusText).toBe('Internal Server Error');
  });
});
Example #24
Source File: delay-requests-to.chromium.test.ts    From playwright-fluent with MIT License 4 votes vote down vote up
describe('delay requests to', (): void => {
  let browser: Browser | undefined = undefined;
  let fakeServer: FakeServer | undefined = undefined;
  beforeAll(() => {
    fakeServer = new FakeServer(1234);
    fakeServer.start();
    //The FakeServer now listens on http://localhost:1234
  });
  afterAll(() => {
    if (fakeServer) {
      fakeServer.stop();
    }
  });
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});
  afterEach(async (): Promise<void> => {
    if (browser) {
      const contexts = browser.contexts();
      await contexts[0].close();
      await browser.close();
    }
  });

  test('should delay request', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({
      headless: true,
    });
    const context = await browser.newContext({
      viewport: null,
      recordHar: { path: `${path.join(__dirname, 'delay-requests-to.test.har')}` },
    });
    const page = await context.newPage();

    const responseBody = {
      prop1: 'foobar',
    };
    const responseHeaders = {
      'foo-header': 'bar',
    };
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/foobar')
        .willReturn(responseBody, 200, responseHeaders);

    const htmlContent = readFileSync(`${path.join(__dirname, 'delay-requests-to.test.html')}`);
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/app')
        .willReturn(htmlContent.toString(), 200);

    const takeAllPredicate = () => false;
    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    await recordRequestsTo('/foobar', takeAllPredicate, page, callback);

    // When
    await SUT.delayRequestsTo('/foobar', 10, page);

    await page.goto('http://localhost:1234/app');
    await sleep(5000);

    // Then
    expect(requests.length).toBe(0);

    await sleep(10000);
    expect(requests.length).toBe(1);
    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    expect(request.url).toContain('?foo=bar');
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(200);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).toMatchObject(responseBody);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.headers['foo-header']).toBe('bar');
  });
});
Example #25
Source File: with-mocks.chromium.test.ts    From playwright-fluent with MIT License 4 votes vote down vote up
describe('with mocks', (): void => {
  let browser: Browser | undefined = undefined;
  let fakeServer: FakeServer | undefined = undefined;
  beforeAll(() => {
    fakeServer = new FakeServer(1240);
    fakeServer.start();
    //The FakeServer now listens on http://localhost:1240
  });
  afterAll(() => {
    if (fakeServer) {
      fakeServer.stop();
    }
  });
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});
  afterEach(async (): Promise<void> => {
    if (browser) {
      await browser.close();
    }
  });

  test('should mock response for GET requests', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({
      headless: true,
    });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    const responseBody = {
      prop1: 'foobar',
    };
    const responseHeaders = {
      'foo-header': 'bar',
    };

    const mockResponseBody = {
      prop1: 'mock-foobar',
    };
    const mockResponseHeaders = {
      'foo-header': 'mock-bar',
    };
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/foobar')
        .willReturn(responseBody, 200, responseHeaders);

    const mock: Partial<SUT.FluentMock> = {
      displayName: 'mock for GET /foobar?foo=bar',
      urlMatcher: (url) => url.includes('/foobar'),
      queryStringMatcher: (queryString) => queryString.foo === 'bar',
      methodMatcher: (method) => method === 'GET',
      enrichResponseHeaders: (headers) => {
        return {
          ...headers,
          ...mockResponseHeaders,
        };
      },
      jsonResponse: () => mockResponseBody,
      status: 200,
    };

    const mockOptions: SUT.WithMocksOptions = {
      onMockFound: (foundMock) => {
        expect(foundMock.displayName).toBe(mock.displayName);
      },
      onMockNotFound: (requestInfo) => {
        // eslint-disable-next-line no-console
        console.log(`mock not found for ${requestInfo.request.url()}`);
      },
      onInternalError: (error) => {
        // eslint-disable-next-line no-console
        console.log(`internal error: ${error}`);
      },
    };
    const htmlContent = readFileSync(`${path.join(__dirname, 'with-mocks.test.html')}`);
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/app')
        .willReturn(htmlContent.toString(), 200);

    const requests: Request[] = [];
    const ignorePredicate = () => false;
    const callback = (request: Request) => requests.push(request);
    await recordRequestsTo('/foobar', ignorePredicate, page, callback);

    const mocks = () => [mock];
    const sharedContext = {};

    // When
    await SUT.withMocks(mocks, () => sharedContext, mockOptions, page);

    await page.goto('http://localhost:1240/app');
    await sleep(3000);

    // Then
    expect(requests.length).toBe(2);
    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    expect(request.url).toContain('?foo=bar');
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(200);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).not.toMatchObject(responseBody);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).toMatchObject(mockResponseBody);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.headers['foo-header']).toBe('mock-bar');
  });
});
Example #26
Source File: get-outdated-mocks.ts    From playwright-fluent with MIT License 4 votes vote down vote up
/**
 * Get outdated mocks from the given unintercepted requests.
 * If a mock is found to be outdated and has provided a method to update its data source,
 * then the update method will be called, so that the mock can update itself the data source of the mocked response.
 * @export
 * @param {Partial<FluentMock>[]} mocks
 * @param {Request[]} requests
 * @param {Partial<WithMocksOptions>} options
 * @param {unknown} [sharedContext={}]
 * @returns {Promise<OutdatedMock[]>}
 */
export async function getOutdatedMocks(
  mocks: Partial<FluentMock>[],
  requests: Request[],
  options: Partial<WithMocksOptions>,
  sharedContext: unknown = {},
): Promise<OutdatedMock[]> {
  const mockOptions: WithMocksOptions = {
    ...defaultMocksOptions,
    ...options,
  };

  const result: OutdatedMock[] = [];
  if (!Array.isArray(mocks)) {
    return [];
  }

  if (mocks.length === 0) {
    return [];
  }

  if (requests.length === 0) {
    throw new Error('No requests provided. Maybe you forgot to record requests.');
  }

  mocks.forEach(validateMock);

  for (let index = 0; index < requests.length; index++) {
    const request = requests[index];
    const requestMethod = request.method() as HttpRequestMethod;
    const url = request.url();
    const queryString = extractQueryStringObjectFromUrl(url) as QueryString;
    const postData = getPostDataOf(request);
    const requestInfos: RequestInfos = { request, queryString, postData, sharedContext };

    const requestResponse = await request.response();

    if (!requestResponse) {
      continue;
    }

    const requestStatus = requestResponse.status();
    if (requestStatus >= 500) {
      continue;
    }

    const mock = mocks
      .map(inferMockResponseTypeIfNeeded)
      .map(spreadMissingProperties)
      .filter((mock) => mock.urlMatcher(url))
      .filter((mock) => mock.methodMatcher(requestMethod))
      .filter((mock) => mock.queryStringMatcher(queryString))
      .filter((mock) => mock.postDataMatcher(postData))
      .filter((mock) => mock.contextMatcher(sharedContext))
      .filter((mock) => mock.customMatcher(requestInfos))
      .pop();

    if (!mock) {
      mockOptions.onMockNotFound(requestInfos);
      continue;
    }

    if (mock.responseType === 'continue') {
      continue;
    }

    const mockedStatus = getMockStatus(mock, requestInfos);
    if (mockedStatus >= 500) {
      continue;
    }

    if (mock.responseType === 'json') {
      try {
        const mockedResponse = mock.jsonResponse(requestInfos);
        const actualResponse = await tryGetContentAsJson(requestResponse);
        mockOptions.onMockFound(mock, requestInfos);
        const isOutdated = !areSameType(mockedResponse, actualResponse);
        if (isOutdated && shouldUpdateMock(mock, requestInfos)) {
          result.push({
            url,
            mock,
            actualResponse,
            mockedResponse,
          });
          mock.updateData(requestInfos, actualResponse);
        }
      } catch (error) {
        mockOptions.onInternalError(error as Error, mock, {
          request,
          queryString,
          postData,
          sharedContext,
        });
      }

      continue;
    }

    if (mock.responseType === 'string' || mock.responseType === 'empty') {
      try {
        const mockedBody = mock.rawResponse(requestInfos);
        const actualBody = await tryGetContentAsText(requestResponse);
        mockOptions.onMockFound(mock, requestInfos);
        if (isJson(actualBody) && shouldUpdateMock(mock, requestInfos)) {
          result.push({
            url,
            mock,
            actualResponse: actualBody,
            mockedResponse: mockedBody,
          });
          mock.updateData(requestInfos, actualBody);
        }
      } catch (error) {
        mockOptions.onInternalError(error as Error, mock, {
          request,
          queryString,
          postData,
          sharedContext,
        });
      }

      continue;
    }

    if (mock.responseType === 'javascript') {
      try {
        const mockedBody = mock.rawResponse(requestInfos);
        const actualBody = await tryGetContentAsText(requestResponse);
        mockOptions.onMockFound(mock, requestInfos);
        const isOutdated = mockedBody !== actualBody;
        if (isOutdated && shouldUpdateMock(mock, requestInfos)) {
          result.push({
            url,
            mock,
            actualResponse: actualBody,
            mockedResponse: mockedBody,
          });
          mock.updateData(requestInfos, actualBody);
        }
      } catch (error) {
        mockOptions.onInternalError(error as Error, mock, {
          request,
          queryString,
          postData,
          sharedContext,
        });
      }

      continue;
    }
  }

  return result;
}
Example #27
Source File: get-missing-mocks.ts    From playwright-fluent with MIT License 4 votes vote down vote up
export async function getMissingMocks(
  mocks: Partial<FluentMock>[],
  requests: Request[],
  sharedContext: unknown = {},
): Promise<MissingMock[]> {
  const result: MissingMock[] = [];

  if (requests.length === 0) {
    throw new Error('No requests provided. Maybe you forgot to record requests.');
  }

  mocks.forEach(validateMock);

  for (let index = 0; index < requests.length; index++) {
    const request = requests[index];
    const method = request.method() as HttpRequestMethod;
    const url = request.url();
    const queryString = extractQueryStringObjectFromUrl(url) as QueryString;
    const postData = getPostDataOf(request);
    const requestResponse = await request.response();
    const requestInfos: RequestInfos = { request, queryString, postData, sharedContext };

    if (!requestResponse) {
      continue;
    }

    const requestStatus = requestResponse.status();
    if (requestStatus >= 300) {
      continue;
    }

    const mock = mocks
      .map(inferMockResponseTypeIfNeeded)
      .map(spreadMissingProperties)
      .filter((mock) => mock.urlMatcher(url))
      .filter((mock) => mock.methodMatcher(method))
      .filter((mock) => mock.queryStringMatcher(queryString))
      .filter((mock) => mock.postDataMatcher(postData))
      .filter((mock) => mock.contextMatcher(sharedContext))
      .filter((mock) => mock.customMatcher(requestInfos))
      .pop();

    if (mock) {
      // TODO : get the response from the mock because it might change the shared context
      continue;
    }

    const mimeType = requestResponse.headers()['content-type'] as MimeType;
    const status = requestResponse.status();

    try {
      const response = await requestResponse.json();
      result.push({
        url,
        method,
        mimeType,
        postData,
        queryString,
        status,
        response,
      });
      continue;
      // eslint-disable-next-line no-empty
    } catch (error) {}

    try {
      const response = await requestResponse.text();
      result.push({
        url,
        method,
        mimeType,
        postData,
        queryString,
        status,
        response,
      });
      continue;
      // eslint-disable-next-line no-empty
    } catch (error) {}

    result.push({
      url,
      method,
      mimeType,
      postData,
      queryString,
      status,
      response: null,
    });
  }

  return result;
}
Example #28
Source File: record-requests-to.chromium.test.ts    From playwright-fluent with MIT License 4 votes vote down vote up
describe('record requests to', (): void => {
  let browser: Browser | undefined = undefined;
  let fakeServer: FakeServer | undefined = undefined;
  beforeAll(() => {
    fakeServer = new FakeServer(1239);
    fakeServer.start();
    //The FakeServer now listens on http://localhost:1239
  });
  afterAll(() => {
    if (fakeServer) {
      fakeServer.stop();
    }
  });
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});
  afterEach(async (): Promise<void> => {
    if (browser) {
      await browser.close();
    }
  });

  test('should record successufull requests', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    const responseBody = {
      prop1: 'foobar',
    };
    const responseHeaders = {
      'foo-header': 'bar',
    };
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/foobar')
        .willReturn(responseBody, 200, responseHeaders);

    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    const takeAllPredicate = () => false;

    // When
    await SUT.recordRequestsTo('/foobar', takeAllPredicate, page, callback);
    await page.goto(`file:${path.join(__dirname, 'record-requests-to.test.html')}`);
    await sleep(3000);

    // Then
    expect(requests.length).toBe(1);

    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    expect(request.url).toContain('?foo=bar');
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(200);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(await request.response!.payload).toMatchObject(responseBody);
  });

  test('should record failed requests 500', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/500')
        .willFail(500);

    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    const takeAllPredicate = () => false;

    // When
    await SUT.recordRequestsTo('/500', takeAllPredicate, page, callback);
    await page.goto(`file:${path.join(__dirname, 'record-failed-requests-500.test.html')}`);
    await sleep(3000);

    // Then
    expect(requests.length).toBe(1);

    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(500);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.statusText).toBe('Internal Server Error');
  });

  test('should encode HTML response', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    const takeAllPredicate = () => false;

    // When
    await SUT.recordRequestsTo('/', takeAllPredicate, page, callback);
    await page.goto(`file:${path.join(__dirname, 'record-requests-to.test.html')}`);
    await sleep(3000);

    // Then
    const htmlPageRequest = requests[0];
    const stringifiedRequest = await stringifyRequest(htmlPageRequest);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).not.toContain('<script>');
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).toContain('&lt;script&gt;');
  });

  test('should record postdata on failed POST requests 502', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({ headless: true });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .post()
        .to('/502')
        .willFail(502);

    const requests: Request[] = [];
    const callback = (request: Request) => requests.push(request);
    const takeAllPredicate = () => false;

    // When
    await SUT.recordRequestsTo('/502', takeAllPredicate, page, callback);
    await page.goto(`file:${path.join(__dirname, 'record-failed-requests-502.test.html')}`);
    await sleep(3000);

    // Then
    expect(requests.length).toBe(1);

    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(502);
    expect(request.postData).toMatchObject({ foo: 'bar' });
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.statusText).toBe('Bad Gateway');
  });
});
Example #29
Source File: on-request-to-respond-with.chromium.test.ts    From playwright-fluent with MIT License 4 votes vote down vote up
describe('on request to respond with', (): void => {
  let browser: Browser | undefined = undefined;
  let fakeServer: FakeServer | undefined = undefined;
  beforeAll(() => {
    fakeServer = new FakeServer(1236);
    fakeServer.start();
    //The FakeServer now listens on http://localhost:1236
  });
  afterAll(() => {
    if (fakeServer) {
      fakeServer.stop();
    }
  });
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  beforeEach((): void => {});
  afterEach(async (): Promise<void> => {
    if (browser) {
      await browser.close();
    }
  });

  test('should mock response', async (): Promise<void> => {
    // Given
    browser = await chromium.launch({
      headless: true,
    });
    const context = await browser.newContext({ viewport: null });
    const page = await context.newPage();

    const responseBody = {
      prop1: 'foobar',
    };
    const mockResponseBody = {
      prop1: 'mock-foobar',
    };
    const responseHeaders = {
      'foo-header': 'bar',
    };
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/foobar')
        .willReturn(responseBody, 200, responseHeaders);

    const htmlContent = readFileSync(
      `${path.join(__dirname, 'on-request-to-respond-with.test.html')}`,
    );
    fakeServer &&
      // prettier-ignore
      fakeServer.http
        .get()
        .to('/app')
        .willReturn(htmlContent.toString(), 200);

    const requests: Request[] = [];
    const takeAllPredicate = () => false;
    const callback = (request: Request) => requests.push(request);
    await recordRequestsTo('/foobar', takeAllPredicate, page, callback);

    // When
    await SUT.onRequestToRespondWith(
      '/foobar',
      { method: 'GET', bypassPredicate: () => false },
      {
        headers: responseHeaders,
        body: mockResponseBody,
      },
      page,
    );

    await page.goto('http://localhost:1236/app');
    await sleep(3000);

    // Then
    expect(requests.length).toBe(1);
    const stringifiedRequest = await stringifyRequest(requests[0]);
    const request = JSON.parse(stringifiedRequest) as RequestInfo;

    expect(request.url).toContain('?foo=bar');
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.status).toBe(200);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).not.toMatchObject(responseBody);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.payload).toMatchObject(mockResponseBody);
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    expect(request.response!.headers['foo-header']).toBe('bar');
  });
});