| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import fetchMock from 'fetch-mock'; |
| import callApi from '@superset-ui/core/src/connection/callApi/callApi'; |
| import parseResponse from '@superset-ui/core/src/connection/callApi/parseResponse'; |
| |
| import { LOGIN_GLOB } from '../fixtures/constants'; |
| |
| describe('parseResponse()', () => { |
| beforeAll(() => { |
| fetchMock.get(LOGIN_GLOB, { csrf_token: '1234' }); |
| }); |
| |
| afterAll(fetchMock.restore); |
| |
| const mockGetUrl = '/mock/get/url'; |
| const mockPostUrl = '/mock/post/url'; |
| const mockErrorUrl = '/mock/error/url'; |
| const mockNoParseUrl = '/mock/noparse/url'; |
| |
| const mockGetPayload = { get: 'payload' }; |
| const mockPostPayload = { post: 'payload' }; |
| const mockErrorPayload = { status: 500, statusText: 'Internal error' }; |
| |
| fetchMock.get(mockGetUrl, mockGetPayload); |
| fetchMock.post(mockPostUrl, mockPostPayload); |
| fetchMock.get(mockErrorUrl, () => Promise.reject(mockErrorPayload)); |
| fetchMock.get(mockNoParseUrl, new Response('test response')); |
| |
| afterEach(fetchMock.reset); |
| |
| it('returns a Promise', () => { |
| const apiPromise = callApi({ url: mockGetUrl, method: 'GET' }); |
| const parsedResponsePromise = parseResponse(apiPromise); |
| expect(parsedResponsePromise).toBeInstanceOf(Promise); |
| }); |
| |
| it('resolves to { json, response } if the request succeeds', async () => { |
| expect.assertions(4); |
| const args = await parseResponse(callApi({ url: mockGetUrl, method: 'GET' })); |
| expect(fetchMock.calls(mockGetUrl)).toHaveLength(1); |
| const keys = Object.keys(args); |
| expect(keys).toContain('response'); |
| expect(keys).toContain('json'); |
| expect(args.json).toEqual(expect.objectContaining(mockGetPayload) as typeof args.json); |
| }); |
| |
| it('throws if `parseMethod=json` and .json() fails', async () => { |
| expect.assertions(3); |
| |
| const mockTextUrl = '/mock/text/url'; |
| const mockTextResponse = |
| '<html><head></head><body>I could be a stack trace or something</body></html>'; |
| fetchMock.get(mockTextUrl, mockTextResponse); |
| |
| let error; |
| try { |
| await parseResponse(callApi({ url: mockTextUrl, method: 'GET' })); |
| } catch (err) { |
| error = err as Error; |
| } finally { |
| expect(fetchMock.calls(mockTextUrl)).toHaveLength(1); |
| expect(error?.stack).toBeDefined(); |
| expect(error?.message).toContain('Unexpected token'); |
| } |
| }); |
| |
| it('resolves to { text, response } if the `parseMethod=text`', async () => { |
| expect.assertions(4); |
| |
| // test with json + bigint to ensure that it was not first parsed as json |
| const mockTextParseUrl = '/mock/textparse/url'; |
| const mockTextJsonResponse = '{ "value": 9223372036854775807 }'; |
| fetchMock.get(mockTextParseUrl, mockTextJsonResponse); |
| |
| const args = await parseResponse(callApi({ url: mockTextParseUrl, method: 'GET' }), 'text'); |
| expect(fetchMock.calls(mockTextParseUrl)).toHaveLength(1); |
| const keys = Object.keys(args); |
| expect(keys).toContain('response'); |
| expect(keys).toContain('text'); |
| expect(args.text).toBe(mockTextJsonResponse); |
| }); |
| |
| it('throws if parseMethod is not null|json|text', async () => { |
| expect.assertions(1); |
| |
| let error; |
| try { |
| await parseResponse( |
| callApi({ url: mockNoParseUrl, method: 'GET' }), |
| 'something-else' as never, |
| ); |
| } catch (err) { |
| error = err; |
| } finally { |
| expect(error.message).toEqual(expect.stringContaining('Expected parseResponse=json')); |
| } |
| }); |
| |
| it('resolves to unmodified `Response` object if `parseMethod=null|raw`', async () => { |
| expect.assertions(3); |
| const responseNull = await parseResponse(callApi({ url: mockNoParseUrl, method: 'GET' }), null); |
| const responseRaw = await parseResponse(callApi({ url: mockNoParseUrl, method: 'GET' }), 'raw'); |
| expect(fetchMock.calls(mockNoParseUrl)).toHaveLength(2); |
| expect(responseNull.bodyUsed).toBe(false); |
| expect(responseRaw.bodyUsed).toBe(false); |
| }); |
| |
| it('rejects if request.ok=false', async () => { |
| expect.assertions(3); |
| const mockNotOkayUrl = '/mock/notokay/url'; |
| fetchMock.get(mockNotOkayUrl, 404); // 404s result in not response.ok=false |
| |
| const apiPromise = callApi({ url: mockNotOkayUrl, method: 'GET' }); |
| |
| let error; |
| try { |
| await parseResponse(apiPromise); |
| } catch (err) { |
| error = err as { ok: boolean; status: number }; |
| } finally { |
| expect(fetchMock.calls(mockNotOkayUrl)).toHaveLength(1); |
| expect(error?.ok).toBe(false); |
| expect(error?.status).toBe(404); |
| } |
| }); |
| }); |