Реактивное тестирование с помощью Jest и Enzyme @ реагирует на google-maps / api и возвращает TypeError: Невозможно прочитать свойство 'maps' из неопределенного - PullRequest
0 голосов
/ 21 февраля 2020

Я пытаюсь протестировать компонент с пакетом @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 и избавился от ошибки? Это хороший подход (издеваться над пакетом)? Или это можно решить любым другим способом? Как я могу проверить, отображается ли карта или маркер вообще?

...