Skip to content
Snippets Groups Projects
Select Git revision
  • 070df56d007cd6c27c1b45d9f26e897397fc40d9
  • main default protected
  • refactor/zod-schema-names
  • renovate/main-redis-5.x
  • chore/update-static-data
  • feat/poetry/supersede-pep621
  • fix/markdown/linking
  • next
  • feat/gnupg
  • fix/36615b-branch-reuse-no-cache
  • chore/punycode
  • refactor/pin-new-value
  • feat/36219--git-x509-signing
  • feat/structured-logger
  • hotfix/39.264.1
  • feat/skip-dangling
  • gh-readonly-queue/next/pr-36034-7a061c4ca1024a19e2c295d773d9642625d1c2be
  • hotfix/39.238.3
  • refactor/gitlab-auto-approve
  • feat/template-strings
  • gh-readonly-queue/next/pr-35654-137d934242c784e0c45d4b957362214f0eade1d7
  • 41.71.0
  • 41.70.3
  • 41.70.2
  • 41.70.1
  • 41.70.0
  • 41.69.1
  • 41.69.0
  • 41.68.0
  • 41.67.0
  • 41.66.3
  • 41.66.2
  • 41.66.1
  • 41.66.0
  • 41.65.1
  • 41.65.0
  • 41.64.4
  • 41.64.3
  • 41.64.2
  • 41.64.1
  • 41.64.0
41 results

http-mock.ts

Blame
  • user avatar
    Michael Kriese authored and GitHub committed
    fd00fd53
    History
    http-mock.ts 3.06 KiB
    import type { Url } from 'url';
    import { afterAll, afterEach, beforeAll } from '@jest/globals';
    // eslint-disable-next-line no-restricted-imports
    import nock from 'nock';
    import { makeGraphqlSnapshot } from './graphql-snapshot';
    
    // eslint-disable-next-line no-restricted-imports
    export type { Scope, ReplyHeaders, Body } from 'nock';
    
    interface RequestLogItem {
      headers: Record<string, string>;
      method: string;
      url: string;
      body?: any;
      graphql?: any;
    }
    
    type BasePath = string | RegExp | Url;
    
    let requestLog: RequestLogItem[] = [];
    let missingLog: string[] = [];
    
    type TestRequest = {
      method: string;
      href: string;
    };
    
    function onMissing(req: TestRequest, opts?: TestRequest): void {
      if (opts) {
        missingLog.push(`  ${opts.method} ${opts.href}`);
      } else {
        missingLog.push(`  ${req.method} ${req.href}`);
      }
    }
    
    export function allUsed(): boolean {
      return nock.isDone();
    }
    
    /**
     *  Clear nock state. Will be called in `afterEach`
     *  @argument throwOnPending Use `false` to simply clear mocks.
     */
    export function clear(throwOnPending = true): void {
      const isDone = nock.isDone();
      const pending = nock.pendingMocks();
      nock.abortPendingRequests();
      nock.cleanAll();
      const missing = missingLog;
      requestLog = [];
      missingLog = [];
      if (missing.length && throwOnPending) {
        throw new Error(`Missing mocks!\n * ${missing.join('\n * ')}`);
      }
      if (!isDone && throwOnPending) {
        throw new Error(`Pending mocks!\n * ${pending.join('\n * ')}`);
      }
    }
    
    export function scope(basePath: BasePath, options?: nock.Options): nock.Scope {
      return nock(basePath, options).on('request', (req) => {
        const { headers, method } = req;
        const url = req.options?.href;
        const result: RequestLogItem = { headers, method, url };
        const requestBody = req.requestBodyBuffers?.[0]?.toString();
    
        if (requestBody && headers['content-type'] === 'application/json') {
          try {
            const body = JSON.parse(requestBody);
            const graphql = makeGraphqlSnapshot(body);
            if (graphql) {
              result.graphql = graphql;
            } else {
              result.body = body;
            }
          } catch (e) {
            result.body = requestBody;
          }
        }
        requestLog.push(result);
      });
    }
    
    export function getTrace(): RequestLogItem[] /* istanbul ignore next */ {
      const errorLines: string[] = [];
      if (missingLog.length) {
        errorLines.push('Missing mocks:');
        errorLines.push(...missingLog);
      }
      if (!nock.isDone()) {
        errorLines.push('Unused mocks:');
        errorLines.push(...nock.pendingMocks().map((x) => `  ${x}`));
      }
      if (errorLines.length) {
        throw new Error(
          [
            'Completed requests:',
            ...requestLog.map(({ method, url }) => `  ${method} ${url}`),
            ...errorLines,
          ].join('\n')
        );
      }
      return requestLog;
    }
    
    // init nock
    beforeAll(() => {
      nock.emitter.on('no match', onMissing);
      nock.disableNetConnect();
    });
    
    // clean nock to clear memory leack from http module patching
    afterAll(() => {
      nock.emitter.removeListener('no match', onMissing);
      nock.restore();
    });
    
    // clear nock state
    afterEach(() => {
      clear();
    });