File size: 2,538 Bytes
936b397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
export class MockAPI {
  private mocked: Map<string, jest.Mock>
  private fetch: jest.Mock
  private originalFetch: null | unknown = null

  constructor() {
    this.mocked = new Map()
    this.fetch = jest.fn()
  }

  private setHandler(
    method: string,
    urlWithoutQueryString: string,
    handler: jest.Mock
  ) {
    this.mocked.set(
      [method.toLowerCase(), urlWithoutQueryString].join(' '),
      handler
    )
  }

  // sets get handler
  public get(
    urlWithoutQueryString: string,
    responseHandler: typeof fetch | Record<string, unknown> | number | null
  ): jest.Mock {
    return this.register('get', urlWithoutQueryString, responseHandler)
  }

  // sets post handler
  public post(
    urlWithoutQueryString: string,
    responseHandler: typeof fetch | Record<string, unknown>
  ): jest.Mock {
    return this.register('post', urlWithoutQueryString, responseHandler)
  }

  private register(
    method: string,
    urlWithoutQueryString: string,
    responseHandler: typeof fetch | Record<string, unknown> | number | null
  ): jest.Mock {
    const handler: typeof fetch =
      typeof responseHandler === 'function'
        ? responseHandler
        : () =>
            new Promise((resolve) =>
              resolve({
                status: 200,
                ok: true,
                json: async () => responseHandler
              } as Response)
            )
    const jestWrappedHandler = jest.fn(handler)
    this.setHandler(method, urlWithoutQueryString, jestWrappedHandler)
    return jestWrappedHandler
  }

  private getHandler(method: string, urlWithoutQueryString: string) {
    return this.mocked.get([method, urlWithoutQueryString].join(' '))
  }

  public clear() {
    this.mocked = new Map()
    this.fetch.mockClear()
  }

  public start() {
    this.originalFetch = global.fetch
    const mockFetch: typeof global.fetch = async (input, init) => {
      if (typeof input !== 'string') {
        throw new Error(`Unmocked request ${input.toString()}`)
      }
      const method = (init?.method ?? 'get').toLowerCase()
      const urlWithoutQueryString = input.split('?')[0]
      const handler = this.getHandler(method, urlWithoutQueryString)
      if (!handler) {
        throw new Error(
          `Unmocked request ${method.toString()} ${input.toString()}`
        )
      }
      return handler(input, init)
    }

    global.fetch = this.fetch.mockImplementation(mockFetch)
    return this
  }

  public stop() {
    global.fetch = this.originalFetch as typeof global.fetch
  }
}