From 891e0b8a913cddb57c9f28896b754883c4dd7d0f Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Mon, 9 Oct 2023 17:01:46 -0700 Subject: [PATCH 1/3] chore: refactored fetch to use Platform.requests. Removed jest-fetch-mock and manually mock fetch responses. --- packages/shared/mocks/src/createResponse.ts | 19 +++++++++++++++++++ packages/shared/mocks/src/index.ts | 2 ++ .../sdk-client/jest-setupFilesAfterEnv.ts | 3 --- packages/shared/sdk-client/package.json | 1 - .../src/evaluation/fetchFlags.test.ts | 11 ++++------- .../sdk-client/src/evaluation/fetchFlags.ts | 8 ++++---- .../sdk-client/src/evaluation/fetchUtils.ts | 4 ++-- 7 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 packages/shared/mocks/src/createResponse.ts diff --git a/packages/shared/mocks/src/createResponse.ts b/packages/shared/mocks/src/createResponse.ts new file mode 100644 index 000000000..ff7a4230e --- /dev/null +++ b/packages/shared/mocks/src/createResponse.ts @@ -0,0 +1,19 @@ +import { Response } from '@common'; + +const createResponse = (remoteJson: any) => { + const response: Response = { + headers: { + get: jest.fn(), + keys: jest.fn(), + values: jest.fn(), + entries: jest.fn(), + has: jest.fn(), + }, + status: 200, + text: jest.fn(), + json: () => Promise.resolve(remoteJson), + }; + return Promise.resolve(response); +}; + +export default createResponse; diff --git a/packages/shared/mocks/src/index.ts b/packages/shared/mocks/src/index.ts index 26896b6d4..17bb171d2 100644 --- a/packages/shared/mocks/src/index.ts +++ b/packages/shared/mocks/src/index.ts @@ -1,5 +1,6 @@ import clientContext from './clientContext'; import ContextDeduplicator from './contextDeduplicator'; +import createResponse from './createResponse'; import { crypto, hasher } from './hasher'; import logger from './logger'; import basicPlatform from './platform'; @@ -8,6 +9,7 @@ import { MockStreamingProcessor, setupMockStreamingProcessor } from './streaming export { basicPlatform, clientContext, + createResponse, crypto, logger, hasher, diff --git a/packages/shared/sdk-client/jest-setupFilesAfterEnv.ts b/packages/shared/sdk-client/jest-setupFilesAfterEnv.ts index 8b19a3da5..7b0828bfa 100644 --- a/packages/shared/sdk-client/jest-setupFilesAfterEnv.ts +++ b/packages/shared/sdk-client/jest-setupFilesAfterEnv.ts @@ -1,4 +1 @@ import '@testing-library/jest-dom'; -import fetchMock from 'jest-fetch-mock'; - -fetchMock.enableMocks(); diff --git a/packages/shared/sdk-client/package.json b/packages/shared/sdk-client/package.json index f2abd6264..d87dfef8e 100644 --- a/packages/shared/sdk-client/package.json +++ b/packages/shared/sdk-client/package.json @@ -50,7 +50,6 @@ "jest": "^29.6.1", "jest-diff": "^29.6.1", "jest-environment-jsdom": "^29.6.1", - "jest-fetch-mock": "^3.0.3", "launchdarkly-js-test-helpers": "^2.2.0", "prettier": "^3.0.0", "ts-jest": "^29.1.1", diff --git a/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts b/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts index 724680eec..36788b742 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts @@ -1,7 +1,5 @@ -import fetchMock from 'jest-fetch-mock'; - import { LDContext } from '@launchdarkly/js-sdk-common'; -import { basicPlatform } from '@launchdarkly/private-js-mocks'; +import { basicPlatform, createResponse } from '@launchdarkly/private-js-mocks'; import Configuration from '../configuration'; import fetchFlags from './fetchFlags'; @@ -23,15 +21,15 @@ describe('fetchFeatures', () => { 'x-launchdarkly-wrapper': 'Rapper/1.2.3', }; + const fetchMock = basicPlatform.requests.fetch as jest.Mock; let config: Configuration; beforeEach(() => { - fetchMock.mockOnce(JSON.stringify(mockResponse)); + fetchMock.mockResolvedValue(createResponse(mockResponse)); config = new Configuration(); }); afterEach(() => { - fetchMock.resetMocks(); jest.resetAllMocks(); }); @@ -61,8 +59,7 @@ describe('fetchFeatures', () => { }); test('withReasons', async () => { - fetchMock.resetMocks(); - fetchMock.mockOnce(JSON.stringify(mockResponseWithReasons)); + fetchMock.mockResolvedValue(createResponse(mockResponseWithReasons)); config = new Configuration({ withReasons: true }); const json = await fetchFlags(sdkKey, context, config, basicPlatform); diff --git a/packages/shared/sdk-client/src/evaluation/fetchFlags.ts b/packages/shared/sdk-client/src/evaluation/fetchFlags.ts index 1b5b22e48..4f1f5ca9a 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchFlags.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchFlags.ts @@ -23,11 +23,11 @@ const fetchFlags = async ( sdkKey: string, context: LDContext, config: Configuration, - platform: Platform, + { encoding, info, requests }: Platform, ): Promise => { - const fetchUrl = createFetchUrl(sdkKey, context, config, platform.encoding!); - const fetchOptions: RequestInit = createFetchOptions(sdkKey, context, config, platform.info); - const response = await fetch(fetchUrl, fetchOptions); + const fetchUrl = createFetchUrl(sdkKey, context, config, encoding!); + const fetchOptions = createFetchOptions(sdkKey, context, config, info); + const response = await requests.fetch(fetchUrl, fetchOptions); return response.json(); }; diff --git a/packages/shared/sdk-client/src/evaluation/fetchUtils.ts b/packages/shared/sdk-client/src/evaluation/fetchUtils.ts index bd34605c0..21ae3c449 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchUtils.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchUtils.ts @@ -1,4 +1,4 @@ -import { defaultHeaders, Encoding, Info, LDContext } from '@launchdarkly/js-sdk-common'; +import { defaultHeaders, Encoding, Info, LDContext, Options } from '@launchdarkly/js-sdk-common'; import Configuration from '../configuration'; @@ -69,7 +69,7 @@ export const createFetchOptions = ( context: LDContext, config: Configuration, info: Info, -): RequestInit => { +): Options => { const { useReport, tags } = config; const headers = defaultHeaders(sdkKey, info, tags); From 1920a58cf7a6df6500ff2d38aaffd82aabdcc3d9 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Mon, 9 Oct 2023 17:06:51 -0700 Subject: [PATCH 2/3] chore: remove redundant dom api comments. --- packages/shared/sdk-client/src/evaluation/fetchFlags.ts | 3 --- packages/shared/sdk-client/src/evaluation/fetchUtils.ts | 2 -- 2 files changed, 5 deletions(-) diff --git a/packages/shared/sdk-client/src/evaluation/fetchFlags.ts b/packages/shared/sdk-client/src/evaluation/fetchFlags.ts index 4f1f5ca9a..567055705 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchFlags.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchFlags.ts @@ -16,9 +16,6 @@ export type Flags = { [k: string]: Flag; }; -/** - * Dom api usage: fetch. - */ const fetchFlags = async ( sdkKey: string, context: LDContext, diff --git a/packages/shared/sdk-client/src/evaluation/fetchUtils.ts b/packages/shared/sdk-client/src/evaluation/fetchUtils.ts index 21ae3c449..ded227f76 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchUtils.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchUtils.ts @@ -3,8 +3,6 @@ import { defaultHeaders, Encoding, Info, LDContext, Options } from '@launchdarkl import Configuration from '../configuration'; /** - * Dom api usage: btoa. - * * In react-native use base64-js to polyfill btoa. This is safe * because the react-native repo uses it too. Set the global.btoa to the encode * function of base64-js. From 5b626ccb5eb8439e791b7a58c0aaa7ffc391c7ed Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Tue, 10 Oct 2023 10:23:19 -0700 Subject: [PATCH 3/3] chore: improve mockFetch implementation. --- packages/shared/mocks/src/createResponse.ts | 19 ----------- packages/shared/mocks/src/index.ts | 4 +-- packages/shared/mocks/src/mockFetch.ts | 32 +++++++++++++++++++ .../src/evaluation/fetchFlags.test.ts | 27 +++++++++------- 4 files changed, 49 insertions(+), 33 deletions(-) delete mode 100644 packages/shared/mocks/src/createResponse.ts create mode 100644 packages/shared/mocks/src/mockFetch.ts diff --git a/packages/shared/mocks/src/createResponse.ts b/packages/shared/mocks/src/createResponse.ts deleted file mode 100644 index ff7a4230e..000000000 --- a/packages/shared/mocks/src/createResponse.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Response } from '@common'; - -const createResponse = (remoteJson: any) => { - const response: Response = { - headers: { - get: jest.fn(), - keys: jest.fn(), - values: jest.fn(), - entries: jest.fn(), - has: jest.fn(), - }, - status: 200, - text: jest.fn(), - json: () => Promise.resolve(remoteJson), - }; - return Promise.resolve(response); -}; - -export default createResponse; diff --git a/packages/shared/mocks/src/index.ts b/packages/shared/mocks/src/index.ts index 17bb171d2..beceb6f47 100644 --- a/packages/shared/mocks/src/index.ts +++ b/packages/shared/mocks/src/index.ts @@ -1,15 +1,15 @@ import clientContext from './clientContext'; import ContextDeduplicator from './contextDeduplicator'; -import createResponse from './createResponse'; import { crypto, hasher } from './hasher'; import logger from './logger'; +import mockFetch from './mockFetch'; import basicPlatform from './platform'; import { MockStreamingProcessor, setupMockStreamingProcessor } from './streamingProcessor'; export { basicPlatform, clientContext, - createResponse, + mockFetch, crypto, logger, hasher, diff --git a/packages/shared/mocks/src/mockFetch.ts b/packages/shared/mocks/src/mockFetch.ts new file mode 100644 index 000000000..0ae07f804 --- /dev/null +++ b/packages/shared/mocks/src/mockFetch.ts @@ -0,0 +1,32 @@ +import { Response } from '@common'; + +import basicPlatform from './platform'; + +const createMockResponse = (remoteJson: any, statusCode: number) => { + const response: Response = { + headers: { + get: jest.fn(), + keys: jest.fn(), + values: jest.fn(), + entries: jest.fn(), + has: jest.fn(), + }, + status: statusCode, + text: jest.fn(), + json: () => Promise.resolve(remoteJson), + }; + return Promise.resolve(response); +}; + +/** + * Mocks basicPlatform fetch. Returns the fetch jest.Mock object. + * @param remoteJson + * @param statusCode + */ +const mockFetch = (remoteJson: any, statusCode: number = 200): jest.Mock => { + const f = basicPlatform.requests.fetch as jest.Mock; + f.mockResolvedValue(createMockResponse(remoteJson, statusCode)); + return f; +}; + +export default mockFetch; diff --git a/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts b/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts index 36788b742..4a5975af9 100644 --- a/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts +++ b/packages/shared/sdk-client/src/evaluation/fetchFlags.test.ts @@ -1,5 +1,5 @@ import { LDContext } from '@launchdarkly/js-sdk-common'; -import { basicPlatform, createResponse } from '@launchdarkly/private-js-mocks'; +import { basicPlatform, mockFetch } from '@launchdarkly/private-js-mocks'; import Configuration from '../configuration'; import fetchFlags from './fetchFlags'; @@ -21,11 +21,11 @@ describe('fetchFeatures', () => { 'x-launchdarkly-wrapper': 'Rapper/1.2.3', }; - const fetchMock = basicPlatform.requests.fetch as jest.Mock; let config: Configuration; + const platformFetch = basicPlatform.requests.fetch as jest.Mock; beforeEach(() => { - fetchMock.mockResolvedValue(createResponse(mockResponse)); + mockFetch(mockResponse); config = new Configuration(); }); @@ -36,7 +36,7 @@ describe('fetchFeatures', () => { test('get', async () => { const json = await fetchFlags(sdkKey, context, config, basicPlatform); - expect(fetchMock).toBeCalledWith( + expect(platformFetch).toBeCalledWith( 'https://sdk.launchdarkly.com/sdk/evalx/testSdkKey1/contexts/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlci1rZXktMSJ9', { method: 'GET', @@ -50,20 +50,23 @@ describe('fetchFeatures', () => { config = new Configuration({ useReport: true }); const json = await fetchFlags(sdkKey, context, config, basicPlatform); - expect(fetchMock).toBeCalledWith('https://sdk.launchdarkly.com/sdk/evalx/testSdkKey1/context', { - method: 'REPORT', - headers: reportHeaders, - body: '{"kind":"user","key":"test-user-key-1"}', - }); + expect(platformFetch).toBeCalledWith( + 'https://sdk.launchdarkly.com/sdk/evalx/testSdkKey1/context', + { + method: 'REPORT', + headers: reportHeaders, + body: '{"kind":"user","key":"test-user-key-1"}', + }, + ); expect(json).toEqual(mockResponse); }); test('withReasons', async () => { - fetchMock.mockResolvedValue(createResponse(mockResponseWithReasons)); + mockFetch(mockResponseWithReasons); config = new Configuration({ withReasons: true }); const json = await fetchFlags(sdkKey, context, config, basicPlatform); - expect(fetchMock).toBeCalledWith( + expect(platformFetch).toBeCalledWith( 'https://sdk.launchdarkly.com/sdk/evalx/testSdkKey1/contexts/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlci1rZXktMSJ9?withReasons=true', { method: 'GET', @@ -77,7 +80,7 @@ describe('fetchFeatures', () => { config = new Configuration({ hash: 'test-hash', withReasons: false }); const json = await fetchFlags(sdkKey, context, config, basicPlatform); - expect(fetchMock).toBeCalledWith( + expect(platformFetch).toBeCalledWith( 'https://sdk.launchdarkly.com/sdk/evalx/testSdkKey1/contexts/eyJraW5kIjoidXNlciIsImtleSI6InRlc3QtdXNlci1rZXktMSJ9?h=test-hash', { method: 'GET',