Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import Tester from './src/Tester';
import TestHookStore from './src/TestHookStore';
import useCavy from './src/useCavy';
import wrap from './src/wrap';
import cavyCreateElement, { setJSXConfig, Fragment } from './src/cavyCreateElement';

const Cavy = {
hook,
Tester,
TestHookStore,
useCavy,
wrap
wrap,
cavyCreateElement,
setJSXConfig,
Fragment
};

module.exports = Cavy;
12 changes: 12 additions & 0 deletions sample-app/CavyDirectory/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[ '@wordpress/babel-plugin-import-jsx-pragma', {
scopeVariable: 'cavyCreateElement',
scopeVariableFrag: 'Fragment',
source: 'cavy',
isDefault: false,
} ],
[ '@babel/plugin-transform-react-jsx', {
pragma: 'cavyCreateElement',
pragmaFrag: 'Fragment',
} ],
]
};
7 changes: 6 additions & 1 deletion sample-app/CavyDirectory/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import React, { Component } from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';

import { Tester, TestHookStore } from 'cavy';
import { Tester, TestHookStore, setJSXConfig } from 'cavy';

import EmployeeDirectoryApp from './app/EmployeeDirectoryApp';

import EmployeeListSpec from './specs/EmployeeListSpec';

const testHookStore = new TestHookStore();

setJSXConfig({
testHookStore,
cavyIdPropName: 'yourPropNameForCavyId'
});

class AppWrapper extends Component {
render() {
return (
Expand Down
2 changes: 2 additions & 0 deletions sample-app/CavyDirectory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"devDependencies": {
"@babel/core": "^7.6.2",
"@babel/runtime": "^7.6.2",
"@babel/plugin-transform-react-jsx": "^7.9.1",
"@wordpress/babel-plugin-import-jsx-pragma": "^2.5.0",
"babel-jest": "^24.9.0",
"jest": "^24.9.0",
"metro-react-native-babel-preset": "^0.56.0",
Expand Down
104 changes: 104 additions & 0 deletions src/cavyCreateElement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import cavy from "./generateTestHook";
import wrap, {isNotReactClass, } from "./wrap";

// Public: Drop in replacement for React.createElement that let cavy
// to hook into element creation reducing normal code impact
// directly collecting component refs when the cavyIdProp is set.
// It automatically wraps function components to add forwardRef
//
// If you configure this in you project the code goes from this:
//
// import React from 'react';
// import { View, Text } from 'react-native';
// import { useCavy, wrap } from 'cavy';
//
// export default ({ data }) => {
// const generateTestHook = useCavy();
// const TestableText = wrap(Text);
//
// return (
// <View>
// <TestableText ref={generateTestHook('title')}>
// {data.title}
// </TestableText>
// </View>
// )
// };
//
// to this:
//
// import React from 'react';
// import { View, Text } from 'react-native';
//
// export default ({ data }) => {//
// return (
// <View>
// <TestableText cavyTestId="title">
// {data.title}
// </TestableText>
// </View>
// )
// };
//
// Eve if the configuration is a bit invasive (replace of jsx transpilation)
// it is simple and keeps you src code more free from cavy implementation
//

// Configuration of jsx element creation
let pragmaConfig = {
testHookStore: null,
cavyIdPropName: 'cavyTestId'
};

// Should be called at the startup of you index.test.js
//import { setJSXConfig } from 'cavy-jsx';
//
// const testHookStore = new TestHookStore();
//
// setJSXConfig({
// testHookStore,
// cavyIdPropName: 'yourPropNameForCavyId'
// });
//
export function setJSXConfig(userConfig) {
if(!userConfig || !userConfig.testHookStore){
throw new Error(`You must set testHookStore in pragma config`);
}
pragmaConfig = {...pragmaConfig, ...userConfig};
}

//
// Simple wrapper of React.createElement that handles element with [cavyIdPropName]
// wrapping function component and collecting its reference in cavy
//
export default function cavyCreateElement(componentType, props, children) {

// Check it the jsx config has been set
if(!pragmaConfig || !pragmaConfig.testHookStore || pragmaConfig.cavyIdPropName){
throw new Error('Configure JSX pragma before using it');
}
// Handle only elements with [cavyIdPropName] set
if (pragmaConfig.cavyIdPropName in props) {
const generateTestHook = cavy(pragmaConfig.testHookStore);
let WrappedType = componentType;
// Auto wraps function components
if (typeof componentType === 'function' && isNotReactClass(componentType)) {
WrappedType = wrap(componentType);
}
let newProps;
if( 'ref' in props){
// If the ref is already set keep it and ads identifier to cavy
newProps = { ...props, ref: generateTestHook(props[pragmaConfig.cavyIdPropName], props.ref) };
}else {
// Otherwise just add identifier
newProps = { ...props, ref: generateTestHook(props[pragmaConfig.cavyIdPropName]) };
}
return React.createElement(WrappedType, newProps, children);
}

return React.createElement.apply(undefined, arguments);
}

// JSX replacement must also contains fragment
export const Fragment = React.Fragment;
2 changes: 1 addition & 1 deletion src/wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export default function wrap(Component) {
// checks here. This code is taken from examples in React source code e.g:
//
// https://github.com/facebook/react/blob/12be8938a5d71ffdc21ee7cf770bf1cb63ae038e/packages/react-refresh/src/ReactFreshRuntime.js#L138
function isNotReactClass(Component) {
export function isNotReactClass(Component) {
return !(Component.prototype && Component.prototype.isReactComponent);
}

Expand Down