From fec6fe13bd3da458bfbd7e002149f2c560bfadb1 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Thu, 28 Dec 2023 10:30:56 -0800 Subject: [PATCH 1/5] chore: added LDProvider tests. --- packages/sdk/react-native/jest.config.json | 9 ----- packages/sdk/react-native/jest.config.ts | 12 +++++++ packages/sdk/react-native/package.json | 13 ++++--- .../src/provider/LDProvider.test.tsx | 36 +++++++++++++++++++ .../react-native/src/provider/LDProvider.tsx | 2 +- packages/sdk/react-native/tsconfig.json | 10 +++--- 6 files changed, 61 insertions(+), 21 deletions(-) delete mode 100644 packages/sdk/react-native/jest.config.json create mode 100644 packages/sdk/react-native/jest.config.ts create mode 100644 packages/sdk/react-native/src/provider/LDProvider.test.tsx diff --git a/packages/sdk/react-native/jest.config.json b/packages/sdk/react-native/jest.config.json deleted file mode 100644 index 6174807746..0000000000 --- a/packages/sdk/react-native/jest.config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "transform": { "^.+\\.ts?$": "ts-jest" }, - "testMatch": ["**/*.test.ts?(x)"], - "testPathIgnorePatterns": ["node_modules", "example", "dist"], - "modulePathIgnorePatterns": ["dist"], - "testEnvironment": "node", - "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], - "collectCoverageFrom": ["src/**/*.ts"] -} diff --git a/packages/sdk/react-native/jest.config.ts b/packages/sdk/react-native/jest.config.ts new file mode 100644 index 0000000000..825f7053ad --- /dev/null +++ b/packages/sdk/react-native/jest.config.ts @@ -0,0 +1,12 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const jestConfig: JestConfigWithTsJest = { + preset: 'ts-jest', + testEnvironment: 'jsdom', + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + testPathIgnorePatterns: ['node_modules', 'example', 'dist'], +}; + +export default jestConfig; diff --git a/packages/sdk/react-native/package.json b/packages/sdk/react-native/package.json index 34cd5b6d7d..fe9555f1ca 100644 --- a/packages/sdk/react-native/package.json +++ b/packages/sdk/react-native/package.json @@ -32,7 +32,7 @@ "start": "rimraf dist && yarn tsw", "lint": "eslint . --ext .ts", "prettier": "prettier --write '**/*.@(js|ts|tsx|json|css)' --ignore-path ../../../.prettierignore", - "test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" jest --ci --runInBand", + "test": "jest", "coverage": "yarn test --coverage", "check": "yarn prettier && yarn lint && yarn build && yarn test", "link-dev": "./link-dev.sh", @@ -50,8 +50,9 @@ "event-target-shim": "^6.0.2" }, "devDependencies": { + "@testing-library/react": "^14.1.2", "@trivago/prettier-plugin-sort-imports": "^4.1.1", - "@types/jest": "^29.5.0", + "@types/jest": "^29.5.11", "@types/react": "^18.2.31", "@typescript-eslint/eslint-plugin": "^6.1.0", "@typescript-eslint/parser": "^6.1.0", @@ -61,15 +62,17 @@ "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.5.0", + "jest": "^29.7.0", "launchdarkly-js-test-helpers": "^2.2.0", "prettier": "^3.0.0", "react": "^18.2.0", + "react-dom": "^18.2.0", "react-native": "^0.73.1", "rimraf": "^5.0.5", - "ts-jest": "^29.1.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", "typedoc": "0.25.0", - "typescript": "5.1.6" + "typescript": "^5.3.3" }, "installConfig": { "hoistingLimits": "workspaces" diff --git a/packages/sdk/react-native/src/provider/LDProvider.test.tsx b/packages/sdk/react-native/src/provider/LDProvider.test.tsx new file mode 100644 index 0000000000..e78b4445eb --- /dev/null +++ b/packages/sdk/react-native/src/provider/LDProvider.test.tsx @@ -0,0 +1,36 @@ +import { render } from '@testing-library/react'; + +import { useLDClient } from '../hooks'; +import ReactNativeLDClient from '../ReactNativeLDClient'; +import LDProvider from './LDProvider'; + +const TestApp = () => { + const ldClient = useLDClient(); + return ( + <> +

ldClient {ldClient ? 'defined' : 'undefined'}

+

context {ldClient.getContext() ? 'defined' : 'undefined'}

+ + ); +}; +describe('LDProvider', () => { + let ldc: ReactNativeLDClient; + + beforeEach(() => { + ldc = new ReactNativeLDClient('mob-test', { sendEvents: false }); + }); + + test('correctly pass LDClient to children', () => { + const { getByText } = render( + + + , + ); + + expect(getByText(/ldclient defined/i)).toBeTruthy(); + expect(getByText(/context undefined/i)).toBeTruthy(); + }); + + test.todo('specified context is identified'); + test.todo('listeners are setup correctly'); +}); diff --git a/packages/sdk/react-native/src/provider/LDProvider.tsx b/packages/sdk/react-native/src/provider/LDProvider.tsx index ec2db27988..18396bbec6 100644 --- a/packages/sdk/react-native/src/provider/LDProvider.tsx +++ b/packages/sdk/react-native/src/provider/LDProvider.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useEffect, useState } from 'react'; +import React, { PropsWithChildren, useEffect, useState } from 'react'; import { type LDContext } from '@launchdarkly/js-client-sdk-common'; diff --git a/packages/sdk/react-native/tsconfig.json b/packages/sdk/react-native/tsconfig.json index 234db0cdb0..0a881597ee 100644 --- a/packages/sdk/react-native/tsconfig.json +++ b/packages/sdk/react-native/tsconfig.json @@ -3,11 +3,8 @@ "allowSyntheticDefaultImports": true, "declaration": true, "declarationMap": true, - "jsx": "react-native", - "lib": [ - "es6", - "dom" - ], + "jsx": "react-jsx", + "lib": ["es6", "dom"], "module": "ES6", "moduleResolution": "node", "noImplicitOverride": true, @@ -21,7 +18,8 @@ "strict": true, "stripInternal": true, "target": "ES2017", - "types": ["jest", "node"] + "types": ["jest", "node"], + "esModuleInterop": true }, "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "example"] } From 7f6f51413db3a95c92895ca33d437690f6545103 Mon Sep 17 00:00:00 2001 From: LaunchDarklyReleaseBot Date: Thu, 28 Dec 2023 14:25:43 -0800 Subject: [PATCH 2/5] chore: Fix client type in context. Add separate tsconfig for tests. Use mock ldclient for tests. Started adding more tests. --- packages/sdk/react-native/jest.config.ts | 7 +++- .../src/ReactNativeLDClient.test.ts | 4 +- .../src/provider/LDProvider.test.tsx | 42 +++++++++++++++++-- .../react-native/src/provider/reactContext.ts | 4 +- packages/sdk/react-native/tsconfig.json | 5 +-- packages/sdk/react-native/tsconfig.test.json | 14 +++++++ 6 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 packages/sdk/react-native/tsconfig.test.json diff --git a/packages/sdk/react-native/jest.config.ts b/packages/sdk/react-native/jest.config.ts index 825f7053ad..a740765ad3 100644 --- a/packages/sdk/react-native/jest.config.ts +++ b/packages/sdk/react-native/jest.config.ts @@ -4,7 +4,12 @@ const jestConfig: JestConfigWithTsJest = { preset: 'ts-jest', testEnvironment: 'jsdom', transform: { - '^.+\\.tsx?$': 'ts-jest', + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.test.json', + }, + ], }, testPathIgnorePatterns: ['node_modules', 'example', 'dist'], }; diff --git a/packages/sdk/react-native/src/ReactNativeLDClient.test.ts b/packages/sdk/react-native/src/ReactNativeLDClient.test.ts index 1bbcb4f0ae..f7c9f717f0 100644 --- a/packages/sdk/react-native/src/ReactNativeLDClient.test.ts +++ b/packages/sdk/react-native/src/ReactNativeLDClient.test.ts @@ -6,11 +6,11 @@ describe('ReactNativeLDClient', () => { let ldc: ReactNativeLDClient; beforeEach(() => { - ldc = new ReactNativeLDClient('mob-test', { sendEvents: false }); + ldc = new ReactNativeLDClient('mobile-key', { sendEvents: false }); }); test('constructing a new client', () => { - expect(ldc.sdkKey).toEqual('mob-test'); + expect(ldc.sdkKey).toEqual('mobile-key'); expect(ldc.config.serviceEndpoints).toEqual({ analyticsEventPath: '/mobile', diagnosticEventPath: '/mobile/events/diagnostic', diff --git a/packages/sdk/react-native/src/provider/LDProvider.test.tsx b/packages/sdk/react-native/src/provider/LDProvider.test.tsx index e78b4445eb..bae8ba56c8 100644 --- a/packages/sdk/react-native/src/provider/LDProvider.test.tsx +++ b/packages/sdk/react-native/src/provider/LDProvider.test.tsx @@ -1,26 +1,47 @@ -import { render } from '@testing-library/react'; +import { act, render } from '@testing-library/react'; + +import type { LDContext, LDOptions } from '@launchdarkly/js-client-sdk-common'; import { useLDClient } from '../hooks'; import ReactNativeLDClient from '../ReactNativeLDClient'; import LDProvider from './LDProvider'; +jest.mock('../ReactNativeLDClient', () => + jest.fn((mobileKey: string, _options: LDOptions) => { + let context: LDContext; + + return { + sdkKey: mobileKey, + identify: jest.fn((c: LDContext) => { + context = c; + return Promise.resolve(); + }), + getContext: jest.fn(() => context), + on: jest.fn(), + }; + }), +); + const TestApp = () => { const ldClient = useLDClient(); return ( <>

ldClient {ldClient ? 'defined' : 'undefined'}

+

mobileKey {ldClient.sdkKey ? ldClient.sdkKey : 'undefined'}

context {ldClient.getContext() ? 'defined' : 'undefined'}

); }; describe('LDProvider', () => { let ldc: ReactNativeLDClient; + let context: LDContext; beforeEach(() => { - ldc = new ReactNativeLDClient('mob-test', { sendEvents: false }); + ldc = new ReactNativeLDClient('mobile-key', { sendEvents: false }); + context = { kind: 'user', key: 'test-user-key-1' }; }); - test('correctly pass LDClient to children', () => { + test('client is correctly set', () => { const { getByText } = render( @@ -28,9 +49,22 @@ describe('LDProvider', () => { ); expect(getByText(/ldclient defined/i)).toBeTruthy(); + expect(getByText(/mobilekey mobile-key/i)).toBeTruthy(); expect(getByText(/context undefined/i)).toBeTruthy(); }); - test.todo('specified context is identified'); + test.only('specified context is identified', async () => { + let output; + await act(async () => { + output = render( + + + , + ); + }); + + expect(output!.getByText(/context defined/i)).toBeTruthy(); + }); + test.todo('listeners are setup correctly'); }); diff --git a/packages/sdk/react-native/src/provider/reactContext.ts b/packages/sdk/react-native/src/provider/reactContext.ts index 2ada7da80d..e1b4526fb8 100644 --- a/packages/sdk/react-native/src/provider/reactContext.ts +++ b/packages/sdk/react-native/src/provider/reactContext.ts @@ -1,9 +1,9 @@ import { createContext } from 'react'; -import { LDClient } from '@launchdarkly/js-client-sdk-common'; +import type ReactNativeLDClient from '../ReactNativeLDClient'; export type ReactContext = { - client: LDClient; + client: ReactNativeLDClient; }; export const context = createContext({ diff --git a/packages/sdk/react-native/tsconfig.json b/packages/sdk/react-native/tsconfig.json index 0a881597ee..e5a9204bee 100644 --- a/packages/sdk/react-native/tsconfig.json +++ b/packages/sdk/react-native/tsconfig.json @@ -18,8 +18,7 @@ "strict": true, "stripInternal": true, "target": "ES2017", - "types": ["jest", "node"], - "esModuleInterop": true + "types": ["node"] }, - "exclude": ["**/*.test.ts", "dist", "node_modules", "__tests__", "example"] + "exclude": ["**/*.test.ts*", "dist", "node_modules", "__tests__", "example"] } diff --git a/packages/sdk/react-native/tsconfig.test.json b/packages/sdk/react-native/tsconfig.test.json new file mode 100644 index 0000000000..2c617dcaa7 --- /dev/null +++ b/packages/sdk/react-native/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "jsx": "react-jsx", + "lib": ["es6", "dom"], + "module": "ES6", + "moduleResolution": "node", + "resolveJsonModule": true, + "rootDir": ".", + "strict": true, + "types": ["jest", "node"] + }, + "exclude": ["dist", "node_modules", "__tests__", "example"] +} From 56f18474786833b95f0935f9cdad7919f85f9433 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Thu, 28 Dec 2023 22:17:21 -0800 Subject: [PATCH 3/5] chore: added more provider tests. --- packages/sdk/react-native/example/app.json | 4 +- .../src/provider/LDProvider.test.tsx | 85 ++++++++++++------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/packages/sdk/react-native/example/app.json b/packages/sdk/react-native/example/app.json index b797a3a7ec..681b269d3b 100644 --- a/packages/sdk/react-native/example/app.json +++ b/packages/sdk/react-native/example/app.json @@ -11,9 +11,7 @@ "resizeMode": "contain", "backgroundColor": "#ffffff" }, - "assetBundlePatterns": [ - "**/*" - ], + "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true, "bundleIdentifier": "com.anonymous.reactnativeexample" diff --git a/packages/sdk/react-native/src/provider/LDProvider.test.tsx b/packages/sdk/react-native/src/provider/LDProvider.test.tsx index bae8ba56c8..2b5c904981 100644 --- a/packages/sdk/react-native/src/provider/LDProvider.test.tsx +++ b/packages/sdk/react-native/src/provider/LDProvider.test.tsx @@ -1,26 +1,14 @@ -import { act, render } from '@testing-library/react'; +import { render } from '@testing-library/react'; import type { LDContext, LDOptions } from '@launchdarkly/js-client-sdk-common'; import { useLDClient } from '../hooks'; import ReactNativeLDClient from '../ReactNativeLDClient'; import LDProvider from './LDProvider'; +import setupListeners from './setupListeners'; -jest.mock('../ReactNativeLDClient', () => - jest.fn((mobileKey: string, _options: LDOptions) => { - let context: LDContext; - - return { - sdkKey: mobileKey, - identify: jest.fn((c: LDContext) => { - context = c; - return Promise.resolve(); - }), - getContext: jest.fn(() => context), - on: jest.fn(), - }; - }), -); +jest.mock('./setupListeners'); +jest.mock('../ReactNativeLDClient', () => jest.fn()); const TestApp = () => { const ldClient = useLDClient(); @@ -33,14 +21,41 @@ const TestApp = () => { ); }; describe('LDProvider', () => { - let ldc: ReactNativeLDClient; + let ldc: any; let context: LDContext; + let mockSetupListeners = setupListeners as jest.Mock; beforeEach(() => { - ldc = new ReactNativeLDClient('mobile-key', { sendEvents: false }); + jest.useFakeTimers(); + (ReactNativeLDClient as jest.Mock).mockImplementation( + (mobileKey: string, _options?: LDOptions) => { + let context: LDContext; + + return { + sdkKey: mobileKey, + identify: jest.fn((c: LDContext) => { + context = c; + return Promise.resolve(); + }), + getContext: jest.fn(() => context), + on: jest.fn(), + logger: { + debug: jest.fn(), + }, + }; + }, + ); + mockSetupListeners.mockImplementation((client: ReactNativeLDClient, setState: any) => { + setState({ client }); + }); + ldc = new ReactNativeLDClient('mobile-key'); context = { kind: 'user', key: 'test-user-key-1' }; }); + afterEach(() => { + jest.resetAllMocks(); + }); + test('client is correctly set', () => { const { getByText } = render( @@ -53,18 +68,30 @@ describe('LDProvider', () => { expect(getByText(/context undefined/i)).toBeTruthy(); }); - test.only('specified context is identified', async () => { - let output; - await act(async () => { - output = render( - - - , - ); - }); + test('specified context is identified', async () => { + const { getByText } = render( + + + , + ); - expect(output!.getByText(/context defined/i)).toBeTruthy(); + expect(mockSetupListeners).toHaveBeenCalledWith(ldc, expect.any(Function)); + expect(ldc.identify).toHaveBeenCalledWith(context); + expect(ldc.getContext()).toEqual(context); + expect(getByText(/context defined/i)).toBeTruthy(); }); - test.todo('listeners are setup correctly'); + test('identify errors are caught', async () => { + (ldc.identify as jest.Mock).mockImplementation(() => { + return Promise.reject('faking error when identifying'); + }); + const { getByText } = render( + + + , + ); + await jest.runAllTimersAsync(); + + expect(ldc.logger.debug).toHaveBeenCalledWith(expect.stringMatching(/identify error/)); + }); }); From 46389a02e9a90bf866cea307bfdf89469fa4a8e0 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Thu, 28 Dec 2023 22:39:49 -0800 Subject: [PATCH 4/5] chore: Added setup listeners tests. Removed release-as config. --- .../src/provider/LDProvider.test.tsx | 4 +-- .../src/provider/setupListeners.test.ts | 32 +++++++++++++++++++ release-please-config.json | 8 ++--- 3 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 packages/sdk/react-native/src/provider/setupListeners.test.ts diff --git a/packages/sdk/react-native/src/provider/LDProvider.test.tsx b/packages/sdk/react-native/src/provider/LDProvider.test.tsx index 2b5c904981..146e4673c7 100644 --- a/packages/sdk/react-native/src/provider/LDProvider.test.tsx +++ b/packages/sdk/react-native/src/provider/LDProvider.test.tsx @@ -8,7 +8,7 @@ import LDProvider from './LDProvider'; import setupListeners from './setupListeners'; jest.mock('./setupListeners'); -jest.mock('../ReactNativeLDClient', () => jest.fn()); +jest.mock('../ReactNativeLDClient'); const TestApp = () => { const ldClient = useLDClient(); @@ -21,7 +21,7 @@ const TestApp = () => { ); }; describe('LDProvider', () => { - let ldc: any; + let ldc: ReactNativeLDClient; let context: LDContext; let mockSetupListeners = setupListeners as jest.Mock; diff --git a/packages/sdk/react-native/src/provider/setupListeners.test.ts b/packages/sdk/react-native/src/provider/setupListeners.test.ts new file mode 100644 index 0000000000..763195e00c --- /dev/null +++ b/packages/sdk/react-native/src/provider/setupListeners.test.ts @@ -0,0 +1,32 @@ +import ReactNativeLDClient from '../ReactNativeLDClient'; +import setupListeners from './setupListeners'; + +import resetAllMocks = jest.resetAllMocks; + +jest.mock('../ReactNativeLDClient'); + +describe('setupListeners', () => { + let ldc: ReactNativeLDClient; + let mockSetState: jest.Mock; + + beforeEach(() => { + mockSetState = jest.fn(); + ldc = new ReactNativeLDClient('mob-test-key'); + }); + + afterEach(() => resetAllMocks()); + + test('change listener is setup', () => { + setupListeners(ldc, mockSetState); + expect(ldc.on).toHaveBeenCalledWith('change', expect.any(Function)); + }); + + test('client is set on change event', () => { + setupListeners(ldc, mockSetState); + + const changeHandler = (ldc.on as jest.Mock).mock.calls[0][1]; + changeHandler(); + + expect(mockSetState).toHaveBeenCalledWith({ client: ldc }); + }); +}); diff --git a/release-please-config.json b/release-please-config.json index d6d4d9560f..9aa9c36498 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -1,16 +1,12 @@ { "packages": { "packages/shared/common": {}, - "packages/shared/sdk-client": { - "bump-minor-pre-major": true - }, + "packages/shared/sdk-client": {}, "packages/shared/sdk-server": {}, "packages/shared/sdk-server-edge": {}, "packages/shared/akamai-edgeworker-sdk": {}, "packages/sdk/cloudflare": {}, - "packages/sdk/react-native": { - "bump-minor-pre-major": true - }, + "packages/sdk/react-native": {}, "packages/sdk/server-node": {}, "packages/sdk/vercel": { "extra-files": ["src/createPlatformInfo.ts"] From 68d3cad3aeddb3949f610bb23c49f15531a0e583 Mon Sep 17 00:00:00 2001 From: Yusinto Ngadiman Date: Thu, 28 Dec 2023 22:44:59 -0800 Subject: [PATCH 5/5] fix: Rollback typedoc. --- packages/sdk/react-native/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sdk/react-native/package.json b/packages/sdk/react-native/package.json index fe9555f1ca..1b536f35e0 100644 --- a/packages/sdk/react-native/package.json +++ b/packages/sdk/react-native/package.json @@ -72,7 +72,7 @@ "ts-jest": "^29.1.1", "ts-node": "^10.9.2", "typedoc": "0.25.0", - "typescript": "^5.3.3" + "typescript": "5.1.6" }, "installConfig": { "hoistingLimits": "workspaces"