Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement jest mocks for react-native. #535

Merged
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
75725a5
v2.0.0
Jul 8, 2024
9e0ea68
v3.0.0
Jul 8, 2024
799200d
v3.1.0
Jul 8, 2024
7833f46
v3.2.0
Jul 8, 2024
db3697f
v3.3.0
Jul 8, 2024
4f04b46
v3.4.0
Jul 8, 2024
d248241
v3.5.0
Jul 8, 2024
4944307
v3.5.1
Jul 8, 2024
f4ee714
v4.0.0
Jul 8, 2024
e8b3649
v4.1.0
Jul 8, 2024
a877514
v4.2.0
Jul 8, 2024
2531181
v4.2.1
Jul 8, 2024
8f9cec6
v4.2.2
Jul 8, 2024
b2657ff
chore: created jest mock test for react-native
Jul 30, 2024
75583b3
chore:added example test app for react-native
Jul 30, 2024
efbf89c
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Jul 30, 2024
60cd0a1
chore: added react-native-jest-example to root package.json
Jul 30, 2024
a5341e6
chore: added @launchdarkly/jest to example app package.json
Jul 30, 2024
ad47e51
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Aug 5, 2024
fbc8178
chore: added workspace:^ to package.json of example and jest root. Al…
professorice Aug 6, 2024
7750714
chore: added @testing-library/react-hooks to jest root package, added…
professorice Aug 6, 2024
3e3252a
chore:updated metro config to include find-yarn-workspace-root
professorice Aug 6, 2024
f737935
chore: Moved @launchdarkly/jest to devDependencies and changed test e…
professorice Aug 6, 2024
26692ce
Linting
kinyoklion Aug 6, 2024
af3fcc4
chore: modified App.tsx and welcome.tsx so that the example app is mo…
professorice Aug 13, 2024
f4b52b9
chrore: modified App.tsx and welcome.tsx to appear more like a normal…
professorice Aug 13, 2024
99c789f
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Aug 15, 2024
d95b070
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Aug 20, 2024
d4bbdeb
feature: added resetLDMocks to reset information stored in the mock.
professorice Aug 23, 2024
c578a19
chore: added "clean script to package json, and changed package json …
professorice Aug 23, 2024
1d92d09
fix: correct errors in jest configuration
professorice Aug 28, 2024
0728fdf
fix: typo in resetLDMocks
professorice Aug 28, 2024
4dc3bf2
chore: changed <Welcome> component to be a normal application
professorice Aug 28, 2024
3ef8baa
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Aug 28, 2024
b32dedf
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Aug 29, 2024
d36a2ad
chore: bumped versions
professorice Sep 13, 2024
1f2163e
fix: boolean flags work with simplified flag logic, but no other flag…
professorice Oct 1, 2024
6e3caf7
fix: MockFlags correctly mocks all types of flags
professorice Oct 8, 2024
3e146e8
chore: added tests for ldClient and boolean flag
professorice Oct 8, 2024
78fd823
chore: added a newline to the end of this files
professorice Oct 28, 2024
3d45157
chore: changed @launchdarkly/react-native-client-sdk from workspace t…
professorice Oct 28, 2024
3f5b48c
Merge branch 'main' into mzafir/react-native-jest-mock
professorice Oct 28, 2024
61aefc7
Update package.json
kinyoklion Oct 28, 2024
a4fad23
Update package.json
kinyoklion Oct 28, 2024
23e7dac
chore: pinned react native version to 10.9.0
professorice Oct 29, 2024
beef6b0
fix: removed getConnectionMode and setConnectionMode to since they ar…
professorice Oct 29, 2024
4fdbdbd
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Oct 29, 2024
e9493ed
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Oct 29, 2024
d2cb48e
chore: changed React Native dependency to patch releases
professorice Oct 29, 2024
0760dab
fix: prettier issue
professorice Oct 29, 2024
7bd782f
fix: removed setConnectionMode test
professorice Oct 29, 2024
210f710
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Oct 31, 2024
2fc00e4
Merge branch 'launchdarkly:main' into mzafir/react-native-jest-mock
professorice Oct 31, 2024
32d4a22
Merge branch 'main' into mzafir/react-native-jest-mock
kinyoklion Nov 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
transform: { '^.+\\.ts?$': 'ts-jest' },
testMatch: ['**/__tests__/**/*test.ts?(x)'],
testEnvironment: 'node',
testEnvironment: 'jsdom',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverageFrom: [
'packages/sdk/server-node/src/**/*.ts',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"packages/store/node-server-sdk-redis",
"packages/store/node-server-sdk-dynamodb",
"packages/telemetry/node-server-sdk-otel",
"packages/tooling/jest"
"packages/tooling/jest",
"packages/tooling/jest/example/react-native-example"
kinyoklion marked this conversation as resolved.
Show resolved Hide resolved
],
"private": true,
"scripts": {
Expand Down
38 changes: 38 additions & 0 deletions packages/tooling/jest/example/react-native-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/

# Native
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo

# vscode
.vscode
36 changes: 36 additions & 0 deletions packages/tooling/jest/example/react-native-example/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { StyleSheet } from 'react-native';
import {
AutoEnvAttributes,
LDProvider,
ReactNativeLDClient,
LDOptions,
} from '@launchdarkly/react-native-client-sdk';
import Welcome from './src/welcome';

const options: LDOptions = {
debug: true,
}
//TODO Set MOBILE_KEY in .env file to a mobile key in your project/environment.
const MOBILE_KEY = 'YOUR_MOBILE_KEY';
const featureClient = new ReactNativeLDClient(MOBILE_KEY, AutoEnvAttributes.Enabled, options);

const userContext = { kind: 'user', key: '', anonymous: true };

export default function App() {
featureClient.identify(userContext).catch((e: any) => console.log(e));

return (
<LDProvider client={featureClient}>
<Welcome />
</LDProvider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
29 changes: 29 additions & 0 deletions packages/tooling/jest/example/react-native-example/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"expo": {
"name": "react-native-jest-example",
"slug": "react-native-jest-example",
"version": "0.0.1",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.anonymous.reactnativejestexample"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.anonymous.reactnativejestexample"
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo', '@babel/preset-typescript'],

};
};
10 changes: 10 additions & 0 deletions packages/tooling/jest/example/react-native-example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// We have to use a custom entrypoint for monorepo workspaces to work.
// https://docs.expo.dev/guides/monorepos/#change-default-entrypoint
import { registerRootComponent } from 'expo';

import App from './App';

// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
preset: 'jest-expo',
setupFiles: ['@launchdarkly/jest/react-native'],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// We need to use a custom metro config for monorepo workspaces to work.
// https://docs.expo.dev/guides/monorepos/#modify-the-metro-config
/**
* @type {import('expo/metro-config')}
*/
const { getDefaultConfig } = require('expo/metro-config');
const path = require('path');

// Find the project and workspace directories
const projectRoot = __dirname;

const findWorkspaceRoot = require('find-yarn-workspace-root');

const workspaceRoot = findWorkspaceRoot(__dirname); // Absolute path or null

const config = getDefaultConfig(projectRoot);

// 1. Watch all files within the monorepo
config.watchFolders = [workspaceRoot];
// 2. Let Metro know where to resolve packages and in what order
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
];
// 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
config.resolver.disableHierarchicalLookup = true;

module.exports = config;
40 changes: 40 additions & 0 deletions packages/tooling/jest/example/react-native-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "react-native-jest-example",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "expo start",
"android": "expo run:android",
"ios": "expo run:ios",
"test": "jest",
"clean": "rm -rf node_modules && rm -rf package-lock.json && rm -rf yarn.lock"
},
"dependencies": {
"@launchdarkly/react-native-client-sdk": "^10.9.0",
"expo": "^51.0.26",
"expo-status-bar": "~1.12.1",
"find-yarn-workspace-root": "^2.0.0",
"react": "^18.2.0",
"react-native": "0.74.5"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@babel/runtime": "^7.25.4",
"@launchdarkly/jest": "workspace:^",
"@react-native/babel-preset": "^0.75.2",
"@testing-library/react-native": "^12.6.1",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"jest": "^29.7.0",
"jest-expo": "^51.0.4",
"react-test-renderer": "^18.2.0",
"typescript": "^5.1.3"
},
"packageManager": "[email protected]",
"installConfig": {
"hoistingLimits": "workspaces"
},
"private": true
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @jest-environment jsdom
*/

import { mockFlags, resetLDMocks } from '@launchdarkly/jest/react-native';
import { screen, render } from '@testing-library/react-native';
import { useLDClient } from '@launchdarkly/react-native-client-sdk';
import Welcome from './welcome';

describe('Welcome component test', () => {

afterEach(() => {
resetLDMocks();
});

test('mock boolean flag correctly', () => {
mockFlags({ 'my-boolean-flag': true });
render(<Welcome />);
expect(screen.getByText('Flag value is true')).toBeTruthy();
});

test('mock ldClient correctly', () => {
const current = useLDClient();

current?.track('event');
expect(current.track).toHaveBeenCalledTimes(1);
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { StyleSheet, Text, View } from 'react-native';
import { useLDClient } from '@launchdarkly/react-native-client-sdk';

export default function Welcome() {

const ldClient = useLDClient();

const flagValue = ldClient.boolVariation('my-boolean-flag', false);

return (
<View style={styles.container}>
<Text>Welcome to LaunchDarkly</Text>
<Text>Flag value is {`${flagValue}`}</Text>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"include": [
"/**/*.ts",
"/**/*.tsx",
"/*.js",
"/*.tsx"
],
"exclude": ["node_modules"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"moduleResolution": "bundler",
"jsx": "react-jsx"
}
}
2 changes: 1 addition & 1 deletion packages/tooling/jest/jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"testMatch": ["**/*.test.ts?(x)"],
"testPathIgnorePatterns": ["node_modules", "example", "dist"],
"modulePathIgnorePatterns": ["dist"],
"testEnvironment": "node",
"testEnvironment": "jsdom",
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
"collectCoverageFrom": ["src/**/*.ts"]
}
9 changes: 9 additions & 0 deletions packages/tooling/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,18 @@
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"react-test-renderer": "^18.3.1",
"rimraf": "^5.0.1",
"ts-jest": "^29.1.0",
"typedoc": "0.25.0",
"typescript": "5.1.6"
},
"dependencies": {
"@launchdarkly/react-native-client-sdk": "workspace:^",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need the version from the package here. Otherwise funky things will happen when this is published.

"@testing-library/react-hooks": "^8.0.1",
"@testing-library/react-native": "^12.7.2",
"@types/lodash": "^4.17.7",
"launchdarkly-react-client-sdk": "^3.4.0",
"react": "^18.3.1"
}
}
72 changes: 71 additions & 1 deletion packages/tooling/jest/src/react-native/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@
import {
ldClientMock,
mockFlags,
mockLDProvider,
mockReactNativeLDClient,
mockUseLDClient,
resetLDMocks,
} from '.';

describe('react-native', () => {
test.todo('Add react-native tests');
afterEach(() => {
resetLDMocks();
});

test('reset LD Mocks', () => {
const current = mockUseLDClient();

current?.track('event');
expect(ldClientMock.track).toHaveBeenCalledTimes(1);

resetLDMocks();
expect(ldClientMock.track).toHaveBeenCalledTimes(0);
});

test('mock boolean flag correctly', () => {
mockFlags({ 'bool-flag': true });
expect(ldClientMock.boolVariation).toBeDefined();
});

test('mock number flag correctly', () => {
mockFlags({ 'number-flag': 42 });
expect(ldClientMock.numberVariation).toBeDefined();
});

test('mock string flag correctly', () => {
mockFlags({ 'string-flag': 'hello' });
expect(ldClientMock.stringVariation).toBeDefined();
});

test('mock json flag correctly', () => {
mockFlags({ 'json-flag': { key: 'value' } });
expect(ldClientMock.jsonVariation).toBeDefined();
});

test('mock LDProvider correctly', () => {
expect(mockLDProvider).toBeDefined();
});

test('mock ReactNativeLDClient correctly', () => {
expect(mockReactNativeLDClient).toBeDefined();
});

test('mock ldClient correctly', () => {
const current = mockUseLDClient();

current?.track('event');
expect(ldClientMock.track).toHaveBeenCalledTimes(1);
});

test('mock ldClient complete set of methods correctly', () => {
expect(ldClientMock.identify).toBeDefined();
expect(ldClientMock.allFlags.mock).toBeDefined();
expect(ldClientMock.close.mock).toBeDefined();
expect(ldClientMock.flush).toBeDefined();
expect(ldClientMock.getContext.mock).toBeDefined();
expect(ldClientMock.off.mock).toBeDefined();
expect(ldClientMock.on.mock).toBeDefined();
expect(ldClientMock.setConnectionMode.mock).toBeDefined();
expect(ldClientMock.track.mock).toBeDefined();
expect(ldClientMock.variation.mock).toBeDefined();
expect(ldClientMock.variationDetail.mock).toBeDefined();
});
});
Loading