mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 06:38:13 +00:00
Summary: Modified the ESLint configuration to only enable the Jest environment for files we consider now tests, which are files with the `-test.js` suffix under `__tests__`. Also enabled some globals for test helpers (any file under `__tests__`). This will allow us to catch misspelled tests, while allowing test helpers to use most Jest APIs. Also disabled the Jasmine environment so people stop using Jasmine APIs and we can rollout Circus soon. Reviewed By: aaronabramov Differential Revision: D13199591 fbshipit-source-id: 12a32cf5835630b9987452b0c33d3f8085001689
168 lines
4.9 KiB
JavaScript
168 lines
4.9 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
* @format
|
|
*/
|
|
|
|
/* eslint-env jest */
|
|
|
|
'use strict';
|
|
|
|
const React = require('React');
|
|
const ReactTestRenderer = require('react-test-renderer');
|
|
|
|
const {Switch, Text, TextInput, VirtualizedList} = require('react-native');
|
|
|
|
import type {
|
|
ReactTestInstance,
|
|
ReactTestRendererNode,
|
|
Predicate,
|
|
} from 'react-test-renderer';
|
|
|
|
function byClickable(): Predicate {
|
|
return withMessage(
|
|
node =>
|
|
// note: <Text /> lazy-mounts press handlers after the first press,
|
|
// so this is a workaround for targeting text nodes.
|
|
(node.type === Text &&
|
|
node.props &&
|
|
typeof node.props.onPress === 'function') ||
|
|
// note: Special casing <Switch /> since it doesn't use touchable
|
|
(node.type === Switch && node.props && node.props.disabled !== true) ||
|
|
(node.instance &&
|
|
typeof node.instance.touchableHandlePress === 'function'),
|
|
'is clickable',
|
|
);
|
|
}
|
|
|
|
function byTestID(testID: string): Predicate {
|
|
return withMessage(
|
|
node => node.props && node.props.testID === testID,
|
|
`testID prop equals ${testID}`,
|
|
);
|
|
}
|
|
|
|
function byTextMatching(regex: RegExp): Predicate {
|
|
return withMessage(
|
|
node => node.props && regex.exec(node.props.children),
|
|
`text content matches ${regex.toString()}`,
|
|
);
|
|
}
|
|
|
|
function enter(instance: ReactTestInstance, text: string) {
|
|
const input = instance.findByType(TextInput);
|
|
input.instance._onChange({nativeEvent: {text}});
|
|
}
|
|
|
|
// Returns null if there is no error, otherwise returns an error message string.
|
|
function maximumDepthError(
|
|
tree: {toJSON: () => ReactTestRendererNode},
|
|
maxDepthLimit: number,
|
|
): ?string {
|
|
const maxDepth = maximumDepthOfJSON(tree.toJSON());
|
|
if (maxDepth > maxDepthLimit) {
|
|
return (
|
|
`maximumDepth of ${maxDepth} exceeded limit of ${maxDepthLimit} - this is a proxy ` +
|
|
'metric to protect against stack overflow errors:\n\n' +
|
|
'https://fburl.com/rn-view-stack-overflow.\n\n' +
|
|
'To fix, you need to remove native layers from your hierarchy, such as unnecessary View ' +
|
|
'wrappers.'
|
|
);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function expectNoConsoleWarn() {
|
|
(jest: $FlowFixMe).spyOn(console, 'warn').mockImplementation((...args) => {
|
|
expect(args).toBeFalsy();
|
|
});
|
|
}
|
|
|
|
function expectNoConsoleError() {
|
|
let hasNotFailed = true;
|
|
(jest: $FlowFixMe).spyOn(console, 'error').mockImplementation((...args) => {
|
|
if (hasNotFailed) {
|
|
hasNotFailed = false; // set false to prevent infinite recursion
|
|
expect(args).toBeFalsy();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Takes a node from toJSON()
|
|
function maximumDepthOfJSON(node: ReactTestRendererNode): number {
|
|
if (node == null) {
|
|
return 0;
|
|
} else if (typeof node === 'string' || node.children == null) {
|
|
return 1;
|
|
} else {
|
|
let maxDepth = 0;
|
|
node.children.forEach(child => {
|
|
maxDepth = Math.max(maximumDepthOfJSON(child) + 1, maxDepth);
|
|
});
|
|
return maxDepth;
|
|
}
|
|
}
|
|
|
|
function renderAndEnforceStrictMode(element: React.Node) {
|
|
expectNoConsoleError();
|
|
return renderWithStrictMode(element);
|
|
}
|
|
|
|
function renderWithStrictMode(element: React.Node) {
|
|
const WorkAroundBugWithStrictModeInTestRenderer = prps => prps.children;
|
|
const StrictMode = (React: $FlowFixMe).StrictMode;
|
|
return ReactTestRenderer.create(
|
|
<WorkAroundBugWithStrictModeInTestRenderer>
|
|
<StrictMode>{element}</StrictMode>
|
|
</WorkAroundBugWithStrictModeInTestRenderer>,
|
|
);
|
|
}
|
|
|
|
function tap(instance: ReactTestInstance) {
|
|
const touchable = instance.find(byClickable());
|
|
if (touchable.type === Text && touchable.props && touchable.props.onPress) {
|
|
touchable.props.onPress();
|
|
} else if (touchable.type === Switch && touchable.props) {
|
|
const value = !touchable.props.value;
|
|
const {onChange, onValueChange} = touchable.props;
|
|
onChange && onChange({nativeEvent: {value}});
|
|
onValueChange && onValueChange(value);
|
|
} else {
|
|
// Only tap when props.disabled isn't set (or there aren't any props)
|
|
if (!touchable.props || !touchable.props.disabled) {
|
|
touchable.instance.touchableHandlePress({nativeEvent: {}});
|
|
}
|
|
}
|
|
}
|
|
|
|
function scrollToBottom(instance: ReactTestInstance) {
|
|
const list = instance.findByType(VirtualizedList);
|
|
list.props && list.props.onEndReached();
|
|
}
|
|
|
|
// To make error messages a little bit better, we attach a custom toString
|
|
// implementation to a predicate
|
|
function withMessage(fn: Predicate, message: string): Predicate {
|
|
(fn: any).toString = () => message;
|
|
return fn;
|
|
}
|
|
|
|
export {byClickable};
|
|
export {byTestID};
|
|
export {byTextMatching};
|
|
export {enter};
|
|
export {expectNoConsoleWarn};
|
|
export {expectNoConsoleError};
|
|
export {maximumDepthError};
|
|
export {maximumDepthOfJSON};
|
|
export {renderAndEnforceStrictMode};
|
|
export {renderWithStrictMode};
|
|
export {scrollToBottom};
|
|
export {tap};
|
|
export {withMessage};
|