Я пытаюсь протестировать компонент с пакетом @react-google-maps/api
. Я получаю сообщение об ошибке: TypeError: Cannot read property 'maps' of undefined
.
Мой компонент:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPersonId, updatePersonSettings, pushPersonMessage } from '../../actions/person';
import TemplatePage from '../templates/TemplatePage';
import Card from '../partials/Card';
import Msg from '../partials/Msg';
import { GoogleMap, Marker } from '@react-google-maps/api';
import markerPosition from'../../img/marker-position.png';
import PlacesAutocomplete, { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import PropTypes from 'prop-types';
import { DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ZOOM } from '../../config/';
export class Settings extends Component {
state = {
lat: DEFAULT_LAT,
lng: DEFAULT_LNG,
zoom: DEFAULT_ZOOM,
address: '',
formSubmitted: false
}
componentDidMount () {
const { lat, lng, zoom } = this.props.auth;
this.setState({
lat: lat !== undefined && lat !== null ? lat : DEFAULT_LAT,
lng: lng !== undefined && lng !== null ? lng : DEFAULT_LNG,
zoom: zoom !== undefined && zoom !== null ? zoom : DEFAULT_ZOOM
});
this.drawMarker();
}
handleOnSubmit = e => {
e.preventDefault();
const settings = {
zoom: this.state.zoom,
lat: this.state.lat,
lng: this.state.lng
}
this.props.updatePersonSettings({ id: this.props.auth.person_id, settings })
}
handleChangeZoom = event => {
this.setState({ zoom: parseInt(event.target.value )});
}
handleChangeAddress = (address) => {
this.setState({ address });
}
handleSelect = (address) => {
geocodeByAddress(address)
.then(results =>
getLatLng(results[0])
.then(function(result) {
this.setState({
lat: result.lat,
lng: result.lng,
})
this.drawMarker()
}.bind(this))
)
.catch(error => console.error('Error', error));
};
handleMapClick = e => {
this.setState({
lat: e.latLng.lat(),
lng: e.latLng.lng(),
});
this.drawMarker();
}
handleMapZoom = (zoom) => {
console.log(zoom)
}
drawMarker = () => {
return <Marker
position={{
lat: parseFloat(this.state.lat),
lng: parseFloat(this.state.lng)
}}
icon={
new window.google.maps.MarkerImage(
markerPosition,
null, /* size is determined at runtime */
null, /* origin is 0,0 */
null, /* anchor is bottom center of the scaled image */
new window.google.maps.Size(48, 48)
)
}
>
</Marker>
}
get msg() {
if(this.props.person !== '') {
return <Msg msg={this.props.person} />
}
return null;
}
render() {
const { status } = this.props.person;
const { lat, lng, zoom, address, formSubmitted } = this.state;
return (
<TemplatePage>
{ this.msg }
<Card title='Settings' padding='large'>
<form className="form" onSubmit={this.handleOnSubmit}>
<div className="form-group">
<label htmlFor="position">Default map position</label>
<div className="google-map google-map__settings">
<GoogleMap
center={{ lat, lng }}
zoom={ zoom }
onClick={ e => this.handleMapClick(e) }
onZoomChanged={(e) => {
console.log('zoom changed')
}}
>
{this.drawMarker()}
<div className="map-constraints-container" />
</GoogleMap>
</div>
</div>
<div className="form-group">
<div className="map-constraints-slider">
<label htmlFor="range">Default map zoom: {zoom}</label>
<input
type="range"
id="zoom"
value={ zoom }
name="zoom"
min="1"
max="18"
onChange={ this.handleChangeZoom }
/>
</div>
</div>
<div className="form-group">
<PlacesAutocomplete
value={address}
onChange={ this.handleChangeAddress }
onSelect={ this.handleSelect }
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input
{...getInputProps({
placeholder: 'Search places...',
className: 'location-search-input',
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? 'suggestion-item--active'
: 'suggestion-item';
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: '#fafafa', cursor: 'pointer' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
<div className="form-group">
<input
type="submit"
value="Update settings"
className="btn btn--primary card__footer--btn-left"
disabled={ formSubmitted && status === 'fetching' ? 'disabled' : null }
/>
</div>
</form>
</Card>
</TemplatePage>
)
}
}
Settings.defaultProps = {
auth: {},
person: {},
fetchPersonId: () => Promise.resolve(),
updatePersonsettings: () => Promise.resolve(),
pushPersonMessage: () => Promise.resolve()
}
Settings.propTypes = {
auth: PropTypes.object,
person: PropTypes.object,
fetchPersonId: PropTypes.func,
updatePersonsettings: PropTypes.func,
pushPersonMessage: PropTypes.func
};
export default connect(
({ auth, person }) => ({ auth, person }),
{ fetchPersonId, updatePersonSettings, pushPersonMessage }
)(Settings);
Мой тест:
import React from 'react';
import { shallow } from 'enzyme';
import { Settings } from '../../../components/pages/Settings';
test('should render settings page', () => {
const wrapper = shallow(<Settings />);
expect(wrapper).toMatchSnapshot();
});
Я прочитал, что для решения таких проблем это лучше всего макетировать пакет. В каком-то другом компоненте я использую пакет ``, который мне удалось смоделировать следующим образом:
const zxcvbn = require.requireActual('zxcvbn');
export default (password = 'test') => {
return zxcvbn(password);
}
Как бы я смоделировал пакет @react-google-maps/api
и избавился от ошибки? Это хороший подход (издеваться над пакетом)? Или это можно решить любым другим способом? Как я могу проверить, отображается ли карта или маркер вообще?