web-dev-qa-db-fra.com

Matériel UI + composant de test enzymatique

J'ai un composant dans React que j'essaye de tester avec Jest, malheureusement, les tests ne passent pas.

Le code du composant:

import React, {Component} from 'react';
import ProductItem from '../ProductItem/ProductItem';
import AppBar from "@material-ui/core/es/AppBar/AppBar";
import Tabs from "@material-ui/core/es/Tabs/Tabs";
import Tab from "@material-ui/core/es/Tab/Tab";
import {connect} from 'react-redux';


class ProductsTabsWidget extends Component {

    state = {
        value: 0
    }

    renderTabs = () => {
        return this.props.tabs.map((item, index) => {
            return item.products.length > 0 ? (<Tab key={index} label={item.title}/>) : false;
        })
    }

    handleChange = (event, value) => {
        this.setState({value});
    };


    renderConentActiveTab = () => {
        if (this.props.tabs[this.state.value]) {
            return this.props.tabs[this.state.value].products.map((productIndex) => {
                return (<ProductItem key={productIndex} {...this.props.products[productIndex]} />);
            });
        }
    }

    render() {
        let tabs = null;
        let content = null;
        if (this.props.tabs) {
            tabs = this.renderTabs();
            content = this.renderConentActiveTab();
        }
        return (
            <div>
                <AppBar position="static" color="default">
                    <Tabs
                        value={this.state.value}
                        onChange={this.handleChange}
                        indicatorColor="primary"
                        textColor="primary"
                        centered
                        scrollButtons="auto"
                    >
                        {tabs}
                    </Tabs>
                </AppBar>
                <div className="productWidget">
                    <div className="wrapper">
                        {content}
                    </div>
                </div>
            </div>
        )
    }
}

const mapStateToProps = state => {
    return {
        products: state.product.products,
    }
}

export default connect(mapStateToProps)(ProductsTabsWidget);

J'ai essayé d'écrire le test approprié pour ce composant, le code est ci-dessous:

import React from 'react';

import {configure, shallow} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import ProductsTabsWidget from "./ProductsTabsWidget";


configure({adapter: new Adapter()});

describe('ProductsTabsWidget - component', () => {
    let wrapper;


    beforeEach(() => {
        wrapper = shallow(<ProductsTabsWidget/>);
    });

    it('renders with minimum props without exploding', () => {
        wrapper.setProps({
            tabs: [],
            products:[]
        });
        expect(wrapper).toHaveLength(1);
    });
})

Mais lorsque je lance le test, je reçois une erreur:

Test suite failed to run

    F:\PRACA\reactiveShop\node_modules\@material-ui\core\es\AppBar\AppBar.js:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import _extends from "@babel/runtime/helpers/builtin/extends";
                                                                                             ^^^^^^

    SyntaxError: Unexpected token import

      at new Script (vm.js:51:7)
      at Object.<anonymous> (src/components/product/ProductsTabsWidget/ProductsTabsWidget.js:3:15)

J'ai essayé de tester avec shallow, mount, render mais cela n'a pas aidé. Qu'est-ce que je rate?

Mon application est créée sur create-react-app.

5
DevQuayle

C'est quelque chose de différent lorsque vous utilisez @material-ui.

Vous devez utiliser les API intégrées de @material-ui. Tels que createMount, createShallow, createRender afin d’utiliser les enzymes shallow, mount & render.

Ces API sont construites sur enzyme, vous ne pouvez donc pas utiliser enzyme directement pour tester @material-ui.


Exemple de rendu Shallow avec @ material-ui

import { createShallow } from '@material-ui/core/test-utils';

describe('<MyComponent />', () => {
  let shallow;

  before(() => {
    shallow = createShallow(); 
  });

  it('should work', () => {
    const wrapper = shallow(<MyComponent />);
  });
});

Référence: Documents officiels de @ material-ui

5
Ritwick Dey

Voici une humble tentative visant à fournir une réponse plus complète du point de vue de create-react-app et @material-ui.

1. Créez setupTests.js directement dans le dossier src et collez le code suivant. 

import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });

2. Ce qui suit est un composant stateless qui utilise des composants material-ui.

import React from "react";
import TextField from "@material-ui/core/TextField";

const SearchField = props => (
   <TextField InputProps={{ disableUnderline: true }} fullWidth
              placeholder={props.placeholder}
              onChange={props.onChange}
   />
);

export default SearchField;

Notez que dans le composant ci-dessus, le composant s'attend à ce que le composant parent transmette les accessoires pour le gestionnaire d'événements placeholder et onChange()

3. En venant au scénario de test pour le composant ci-dessus, nous pouvons écrire soit d'une manière suggérée par material-ui, soit dans un style pure enzyme. Les deux vont marcher.

Pure Enzyme Style 

import React from "react";
import { mount } from "enzyme";
import TextField from "@material-ui/core/TextField";
import SearchField from "../SearchField";
describe("SearchField Enzyme mount() ", () => {
  const fieldProps = {
    placeholder: "A placeholder",
    onChange: jest.fn()
  };
  const Composition = props => {
    return <SearchField {...fieldProps} />;
  };

  it("renders a <TextField/> component with expected props", () => {
    const wrapper = mount(<Composition />);
    expect(wrapper.childAt(0).props().placeholder).toEqual("A placeholder");
    expect(wrapper.childAt(0).props().onChange).toBeDefined();
  });
  it("should trigger onChange on <SearchField/> on key press", () => {
    const wrapper = mount(<Composition />);
    wrapper.find("input").simulate("change");
    expect(fieldProps.onChange).toHaveBeenCalled();
  });
  it("should render <TextField />", () => {
    const wrapper = mount(<Composition />);
    expect(wrapper.find(TextField)).toHaveLength(1);
    expect(wrapper.find(TextField).props().InputProps.disableUnderline).toBe(
      true
    );
  });
});

Material UI style

import React from "react";
import { createMount } from "@material-ui/core/test-utils";
import TextField from "@material-ui/core/TextField";
import SearchField from "../SearchField";
describe("SearchField", () => {
  let mount;
  const fieldProps = {
    placeholder: "A placeholder",
    onChange: jest.fn()
  };
  beforeEach(() => {
    mount = createMount();
  });

  afterEach(() => {
    mount.cleanUp();
  });

  it("renders a <TextField/> component with expected props", () => {
    const wrapper = mount(<SearchField {...fieldProps} />);
    expect(wrapper.props().placeholder).toEqual("A placeholder");
    expect(wrapper.props().onChange).toBeDefined();
  });
  it("should trigger onChange on <SearchField/> on key press", () => {
    const wrapper = mount(<SearchField {...fieldProps} />);
    wrapper.find("input").simulate("change");
    expect(fieldProps.onChange).toHaveBeenCalled();
  });
});

5. L'erreur que vous obtenez est due au fait que babel n'obtient pas une chance de traiter votre fichier. Le create-react-app s'attend à ce que vous exécutiez des tests tels que yarn run test et non comme jest your/test/file.js. Si vous utilisez ce dernier, babel ne sera pas employed

Si vous souhaitez utiliser jest pour exécuter le fichier, vous devez écrire un fichier jest.config.js ou configurer jest dans le fichier package.json afin qu'il utilise babel-jest + other babel dependencies pour transcrire votre code avant que jest ne tente d'exécuter des tests.

Hier, j'étais dans le même bateau lorsque j'ai essayé d'utiliser @material-ui pour la première fois et que je suis venu ici pour obtenir une réponse plus complète.

0
Firefly

Quelque chose comme ça a fonctionné pour moi:

import {createMount} from '@material-ui/core/test-utils';

const WrappedComponent = () => 
    <MUIThemeStuffEtc>
        <MyComponent />
    </MUIThemeStuffEtc>

const render = createMount();
const wrapper = render(<WrappedComponent />);

const state = wrapper.find(MyComponent).instance().wrappedInstance.state
0
rekarnar