I'm using React, Enzyme and Jest. This is my connect component which only renders a button
import { connect } from 'react-redux';
import { withStyles } from '#material-ui/core/styles';
import PlaidLinkButton from 'react-plaid-link-button';
const plaidEnv = process.env.REACT_APP_PLAID_ENV
export class ConnectStep extends Component {
handleOnSuccessBroker = async (token, metadata) => {
//unimportant
};
render() {
const { classes } = this.props;
return (
<PlaidLinkButton
buttonProps={{
className: classes.connectButton,
id: 'button',
}}
plaidLinkProps={{
clientName: '###',
key: '###',
env: plaidEnv,
product: ['transactions'],
onSuccess: this.handleOnSuccessBroker,
token: ''
}}
>
{this.props.plaid.accounts.length > 0 ? 'CONNECTED' : 'Start'}
</PlaidLinkButton>
);
}
}
As you can see Im importing PlaidLinkButton but jest throws this error:
###/node_modules/react-plaid-link-button/dist/react-plaid-link-button/react-plaid-link-button.js:19
import PropTypes from 'prop-types';
^^^^^^^^^
SyntaxError: Unexpected identifier
4 |
5 | import { setPlaid } from '../../../actions/actions';
> 6 | import PlaidLinkButton from 'react-plaid-link-button';
What am I missing? I made successful test suites for other components that also import modules. But this one in particular is giving me problems.
import PropTypes from 'prop-types';
^^^^^^^^^
SyntaxError: Unexpected identifier
This is likely an issue related to your Jest/Babel config because the modules aren't getting compiled correctly. You'd need to remove anywhere you set the Babel option modules to false.
Related: https://github.com/facebook/react/issues/14399
Related
I'm learning about react testing but I have some issues.
I'm trying to test a functional component in react
import React, {useState, useEffect, useContext} from 'react';
import DayPicker from 'react-day-picker/DayPickerInput';
import MomentLocaleUtils, {
formatDate,
parseDate,
} from 'react-day-picker/moment';
import 'react-day-picker/lib/style.css';
import 'moment/locale/pt-br';
import Swal from 'sweetalert2';
import $ from 'jquery';
import api from 'config/api';
import {InvestmentsContext} from 'context/InvestmentsContext';
export default props => {
const [investments, setInvestments] = useContext(InvestmentsContext);
const [investment, setInvestment] = useState({
type: 'RENDA_FIXA',
value: '',
date: new Date(),
});
...
return (
<>
<form onSubmit={e => save(e, investment)} className="investment-form">
...
</form>
</>
);
};
But i'm getting this error when I try to test it with Jest and Enzyme
TypeError: Invalid attempt to destructure non-iterable instance
13 | import {InvestmentsContext} from 'context/InvestmentsContext';
14 |
> 15 | export default props => {
| ^
My Spec:
import React from 'react';
import {configure, mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Form from 'components/investment/Form';
configure ({adapter: new Adapter ()});
let wrapper;
describe ('<Form />', () => {
beforeEach (() => {
wrapper = mount (<Form onCountChange={'a'} />);
});
it ('button should be disabled if investment value is empty', () => {
expect (wrapper.find ('.btn-action')).toHaveAttribute ('disabled');
});
});
Why am I getting this? Due to my component be a function?
After updating to Expo sdk 34, I am getting this error:
TypeError: Asset.fromModule is not a function. (In 'Asset.fromModule(image)', 'Asset.fromModule' is undefined).
I have ran the cod-mod which Expo recommends using this command: npx expo-codemod sdk33-imports src when updating to new versions, and I've also tried changing import * as Asset from 'expo-asset'; to import { Asset } from 'expo-asset', but neither of these changes have fixed this warning.
This is my App.js
import React from 'react';
import { Dimensions, Alert } from 'react-native';
import {Root} from 'native-base';
import {createRootNavigator} from './router';
import {configureFontAwesomePro} from "react-native-fontawesome-pro";
import Geocoder from 'react-native-geocoding';
import { AppLoading } from 'expo';
import * as Asset from 'expo-asset';
import * as Font from 'expo-font';
import Api from './services/api';
import Moment from 'moment';
import Config from './config/config';
import './i18n';
import store from './store/store';
import i18n from 'i18n-js';
configureFontAwesomePro();
const RootNav = createRootNavigator();
function cacheImages(images) {
return images.map(image => {
if (typeof image === 'string') {
return Image.prefetch(image);
} else {
return Asset.fromModule(image).downloadAsync();
}
});
}
function cacheFonts(fonts) {
return fonts.map(font => Font.loadAsync(font));
}
export default class App extends React.Component {
constructor() {
super();
this.state = {
isReady: false
};
}
async _loadAssetsAsync() {
const imageAssets = cacheImages([
require('./assets/images/splash.png'),
require('./assets/images/splash-logo.png'),
require('./assets/images/hotdrink.png'),
require('./assets/images/insta.png'),
require('./assets/images/twitter.png'),
require('./assets/images/facebook.png'),
require('./assets/images/yelp.png'),
]);
store.updatePrefix(Expo.Linking.makeUrl('/'));
const fontAssets = cacheFonts(
[{
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("#expo/vector-icons")
}]
);
await Promise.all([...imageAssets, ...fontAssets]);
}
render() {
if (!this.state.isReady) {
return (<AppLoading
startAsync={this._loadAssetsAsync}
onFinish={() => this.setState({isReady: true})}
onError={(error) => console.log('app js: ', error)}
/>)
}
return (
<Root>
<RootNav uriPrefix={store.prefix} />
</Root>
)
}
}
Geocoder.init(Config.google_maps_key);
Edit:
After making the changes suggested below:
- changing the import of asset from import * as Asset from 'expo-asset'; to import { Asset } from 'expo-asset';
- I am now getting this error, which is slightly different than the one
I originally wrote about
- TypeError: asset.downloadAsync is not a function. (In 'asset.downloadAsync()', 'asset.downloadAsync' is undefined).
Seems like this should just be import { Asset } from "expo-asset"; instead of import * as Asset from 'expo-asset';.
I figured this out since that import statement looked odd, so I copied your App.js into a fresh create-react-native-app install, added a few of the dependencies and commented out the bits that seemed unrelated or references to files that aren't posted here. I got the TypeError: Asset.fromModule is not a function. (In 'Asset.fromModule(image)', 'Asset.fromModule' is undefined) warning, changed the import line, and the app loaded (it didn't do much, but with so much commented out that was expected).
The import statement is pretty easy to accidentally get wrong without actually generating an errors on the import line, may be worth refreshing on how as works when using it, see the docs on MDN
Run this in your root folder:
expo install expo-asset
And then run
expo start -c
just to be sure.
I am trying to use the react-monaco-editor npm package component to import the monaco editor into my react application. These are the version of the packages I downloaded from npm:
"react-monaco-editor": "^0.13.0",
"#types/react-monaco-editor": "^0.10.0"
Everything seems to be working great (Less, html, javascript and even doing requires in javascript), except I am getting syntax errors saying that I can't find modules I am importing or namespaces (such as Electron). This is an example error of one of the typescript files trying to import a module and also the error for not being able to find the Electron namespace.
This is the same code in VS Code with their usage of the monaco editor:
This is the example of the code I am using where I am referencing the MonacoEditor component.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import MonacoEditor from 'react-monaco-editor';
import { IEditorFile, IEditorFilePassedProps, IEditorFileReduxProps } from './editor-file-interfaces';
import { IReduxState } from '../../shared/interfaces/redux-state';
import './editor-file.less';
class EditorFile extends Component<IEditorFile, any> {
editor = null;
constructor(props: IEditorFile) {
super(props);
this.state = {
code: this.props.file.content
};
}
componentWillReceiveProps(nextProps: any) {
if (typeof nextProps.file !== undefined) {
this.setState({ code: nextProps.file.content });
}
}
render() {
const code = this.state.code;
const options = {
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: false,
cursorStyle: 'line',
automaticLayout: false
};
return (
<div>
<div className='file-content-container'>
<MonacoEditor
language={this.props.file.fileType}
value={code}
theme='vs-dark'
options={options}
/>
</div>
</div>
);
}
}
const mapStateToProps = (state: IReduxState): IEditorFileReduxProps => {
return {
fileExplorerInfo: state.fileExplorer
};
};
export default connect<IEditorFileReduxProps, null, IEditorFilePassedProps, IReduxState>(mapStateToProps)(EditorFile);
The language that is being passed into the MonacoEditor component is "typescript" for the tsx/ts files. I am not sure how to go about this so any help is greatly appreciated!
ESlint is giving me this warning when I am compiling my code. We are using the AirBNB config.
import React from 'react';
import { Link } from 'react-router-dom';
const ProfileInterestSkillButtons = ({
tags, title, user, member,
}) => {
return (
<div>
{title}
</div>
);
};
export default ProfileInterestSkillButtons;
Your component is using a prop named tags that it is receiving from its parent component.
ESLint is just warning you to define a type check for that prop in the component where you are using it. You can do that by either using PropTypes or by using flow.
Simple example using PropType would be:
... // other imports
import PropTypes from 'prop-types';
... // your component declaration
ProfileInterestSkillButtons.propTypes = {
tags: PropTypes.array.isRequired,
title: PropTypes.string.isRequired,
... // and more
};
export default ProfileInterestSkillButtons;
PropType: https://reactjs.org/docs/typechecking-with-proptypes.html
Flow: https://flow.org/en/docs/react/
Using Flow
Quick recipe to type-check props using flow.
// #flow
import React from 'react';
import type { Node } from 'react';
import { SafeAreaView, ScrollView, StatusBar, StyleSheet } from 'react-native';
const ScreenDefaultView = (props: { layout: Node, scrollableLayout?: boolean } ) => {
const layout = props.layout;
const scrollableLayout = props.scrollableLayout;
return ( ...
Note: in order to add Optional Parameters or also called in Flow maybe type just add a ?.
// scrollableLayout? is optional note the ?
props: { layout: Node, scrollableLayout?: boolean }
Flow Documentation
Predicate Functions
I've just moved some components I use in multiple projects to a private NPM module. For some reason though my build process is now generating the following errors for all the components that were moved:
Module parse failed:
C:\xampp\htdocs\wizer\packages\wizer-atoms\components\Textarea\index.js
Unexpected token (7:21) You may need an appropriate loader to handle
this file type.
Do all NPM modules need to be transpiled by default? Because I did not change any thing to the content. And moving the file back resolves the error.
Example component
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import classnames from "classnames";
class Textarea extends PureComponent {
render() {
const { className, ...otherProps } = this.props;
return <textarea className={classnames("FORM__textfield", className)} />;
}
}
export default Textarea;
My index.js file of the NPM module
const req = require.context(".", true, /\.\/components\/[^/]+\/index\.js/);
req.keys().forEach((key) => {
const componentName = key.replace(/^.+\/([^/]+)\/index\.js/, "$1");
module.exports[componentName] = req(key).default;
});
This basically takes all files in the components directory and exports it. This allows me to use import { Textarea } from wizer-atoms;.