Skip to content

Commit

Permalink
Add testingLibrary config option (#104)
Browse files Browse the repository at this point in the history
* feature: 'testingLibrary' configure option

* docs: add options to docs

* docs: more docs

* refactore: merge `render`, `cleanup` options with `testingLibrary`

* refactor: code review changes

* refactor: code review changes

* chore: added changeset
mdjastrzebski authored Jul 25, 2022
1 parent 0ea5c3c commit 7ad802b
Showing 7 changed files with 123 additions and 45 deletions.
7 changes: 7 additions & 0 deletions .changeset/ten-ducks-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'example-native': minor
'@callstack/reassure-measure': minor
'reassure': minor
---

`testingLibrary` configuration option that replaces `render` and `cleanup` options
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -70,12 +70,25 @@ Using npm
npm install --save-dev reassure
```

You will also need a working [React Native Testing Library](https://github.com/callstack/react-native-testing-library#installation) and [Jest](https://jestjs.io/docs/getting-started) setup.
You will also need a working [Jest](https://jestjs.io/docs/getting-started) setup as well as one of either [React Native Testing Library](https://github.com/callstack/react-native-testing-library#installation) or [React Testing Library](https://testing-library.com/docs/react-testing-library/intro).

> **Note**: React Native Testing Library is fully supported, while React Testing Library in beta stage.
You can check our example projects:
- [React Native (CLI)](https://github.com/callstack/reassure/tree/main/examples/native)
- [React Native (Expo)](https://github.com/callstack/reassure/tree/main/examples/native-expo)

Reassure will try to detect which Testing Library you have installed. In case both React Native Testing Library and React Testing Library are present it will
warn you about that and give a precedence to React Native Testing Library. You can explicitly specify Testing Library to by used by using [`configure`](#configure-function) option:

```
configure({ testingLibrary: 'react-native' })
// or
configure({ testingLibrary: 'react' })
```

You should set it in your Jest setup file and you can override it in particular test files if needed.

### Writing your first test

Now that the library is installed, you can write you first test scenario in a file with `.perf-test.js`/`.perf-test.tsx` extension:
@@ -285,7 +298,7 @@ type Config = {
dropWorst?: number;
outputFile?: string;
verbose?: boolean;
render?: typeof render;
testingLibrary?: 'react-native' | 'react' | { render: (component: React.ReactElement<any>) => any, cleanup: () => any }
};
```

@@ -295,16 +308,15 @@ const defaultConfig: Config = {
dropWorst: 1,
outputFile: '.reassure/current.perf',
verbose: false,
render, // render fn from RNTL
testingLibrary: undefined, // Will try auto-detect first RNTL, then RTL
};
```

**`runs`**: number of repeated runs in a series per test (allows for higher accuracy by aggregating more data). Should be handled with care.
**`dropWorst`**: number of worst dropped results from the series per test (used to remove test run outliers)
dropWorst
**`outputFile`**: name of the file the records will be saved to
**`verbose`**: make Reassure log more, e.g. for debugging purposes
**`render`**: your custom `render` function used to render React components
**`testingLibrary`**: where to look for `render` and `cleanup` functions, supported values `'react-native'`, `'react'` or object providing custom `render` and `cleanup` functions

#### `configure` function

5 changes: 4 additions & 1 deletion examples/native/jest.config.js
Original file line number Diff line number Diff line change
@@ -4,7 +4,10 @@
*/

module.exports = {
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
setupFilesAfterEnv: [
'@testing-library/jest-native/extend-expect',
'./jestSetup.js',
],
preset: '@testing-library/react-native',
clearMocks: true,
};
3 changes: 3 additions & 0 deletions examples/native/jestSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { configure } from 'reassure';

configure({ testingLibrary: 'react-native', verbose: true });
29 changes: 5 additions & 24 deletions packages/reassure-measure/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,22 @@
type Render = (component: React.ReactElement<any>) => any;
type Cleanup = () => void;
export type TestingLibrary = 'react' | 'react-native' | { render: Render; cleanup: Cleanup };

let defaultRender;
let defaultCleanup;

try {
// eslint-disable-next-line import/no-extraneous-dependencies
const RNTL = require('@testing-library/react-native');
defaultRender = RNTL.render;
defaultCleanup = RNTL.cleanup;
} catch (error) {
try {
// eslint-disable-next-line import/no-extraneous-dependencies
const RTL = require('@testing-library/react');
defaultRender = RTL.render;
defaultCleanup = RTL.cleanup;
} catch (error) {
console.warn("Reassure: couldn't find neither @testing-library/react-native nor @testing-library/react");
}
}
export type Render = (component: React.ReactElement<any>) => any;
export type Cleanup = () => void;

type Config = {
runs: number;
dropWorst: number;
outputFile: string;
verbose: boolean;
render?: Render;
cleanup?: Cleanup;
testingLibrary?: TestingLibrary;
};

const defaultConfig: Config = {
runs: 10,
dropWorst: 1,
outputFile: process.env.OUTPUT_FILE ?? '.reassure/current.perf',
verbose: false,
render: defaultRender,
cleanup: defaultCleanup,
testingLibrary: undefined,
};

export let config = defaultConfig;
19 changes: 4 additions & 15 deletions packages/reassure-measure/src/measure.tsx
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
import * as math from 'mathjs';
import { config } from './config';
import { showFlagsOuputIfNeeded, writeTestStats } from './output';
import { resolveTestingLibrary } from './testingLibrary';
import type { MeasureRenderResult } from './types';

export interface MeasureOptions {
@@ -34,19 +35,7 @@ export async function measureRender(ui: React.ReactElement, options?: MeasureOpt

showFlagsOuputIfNeeded();

if (!config.render) {
console.error(
'❌ Reassure: unable to find "render" function. Do you have "@testing-library/react-native" installed?'
);
throw new Error('Unable to find "render" function');
}

if (!config.cleanup) {
console.error(
'❌ Reassure: unable to find "cleanup" function. Do you have "@testing-library/react-native" installed?'
);
throw new Error('Unable to find "cleanup" function');
}
const { render, cleanup } = resolveTestingLibrary();

for (let i = 0; i < runs + dropWorst; i += 1) {
let duration = 0;
@@ -62,7 +51,7 @@ export async function measureRender(ui: React.ReactElement, options?: MeasureOpt
}
};

const screen = config.render(
const screen = render(
<React.Profiler id="Test" onRender={handleRender}>
{wrappedUi}
</React.Profiler>
@@ -72,7 +61,7 @@ export async function measureRender(ui: React.ReactElement, options?: MeasureOpt
await scenario(screen);
}

config.cleanup();
cleanup();

isFinished = true;
global.gc?.();
83 changes: 83 additions & 0 deletions packages/reassure-measure/src/testingLibrary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { config, Render, Cleanup } from './config';

type TestingLibraryApi = {
render: Render;
cleanup: Cleanup;
};

let RNTL: TestingLibraryApi | undefined;
try {
// eslint-disable-next-line import/no-extraneous-dependencies
RNTL = require('@testing-library/react-native');
} catch (error) {
// Do nothing
}

let RTL: TestingLibraryApi | undefined;
try {
// eslint-disable-next-line import/no-extraneous-dependencies
RTL = require('@testing-library/react');
} catch (error) {
// Do nothing
}

export function resolveTestingLibrary(): TestingLibraryApi {
// Explicit testing library option
if (config.testingLibrary) {
if (config.testingLibrary === 'react-native') {
if (!RNTL) {
throw new Error(`Reassure: unable to import '@testing-library/react-native' dependency`);
}

if (config.verbose) console.log(`Reassure: using '@testing-library/react-native' to render components`);
return RNTL;
}

if (config.testingLibrary === 'react') {
if (!RTL) {
throw new Error(`Reassure: unable to import '@testing-library/react' dependency`);
}

if (config.verbose) console.log(`Reassure: using '@testing-library/react' to render components`);
return RTL;
}

if (
typeof config.testingLibrary === 'object' &&
typeof config.testingLibrary.render === 'function' &&
typeof config.testingLibrary.cleanup === 'function'
) {
if (config.verbose) console.log(`Reassure: using custom 'render' and 'cleanup' functions to render components`);
return config.testingLibrary;
}

throw new Error(
`Reassure: unsupported 'testingLibrary' value. Please set 'testingLibrary' to one of following values: 'react-native', 'react' or { render, cleanup }.`
);
}

// Testing library auto-detection
if (RNTL != null && RTL != null) {
console.warn(
`Reassure: both '@testing-library/react-native' and '@testing-library/react' are installed. Using '@testing-library/react-native' by default.` +
`\nYou can resolve this warning by explicitly calling 'configure({ testingLibrary: 'react-native' })' or 'configure({ testingLibrary: 'react' })' in your test setup file.`
);

return RNTL;
}

if (RNTL != null) {
if (config.verbose) console.log(`Reassure: using '@testing-library/react-native' to render components`);
return RNTL;
}

if (RTL != null) {
if (config.verbose) console.log(`Reassure: using '@testing-library/react' to render components`);
return RTL;
}

throw new Error(
`Reassure: unable to import neither '@testing-library/react-native' nor '@testing-library/react'.` +
`\nAdd either of these testing libraries to your 'package.json'`
);
}

0 comments on commit 7ad802b

Please sign in to comment.