32

So I was trying to learn about testing in React and I have this: Button.js and Button.test.js

The question is commented along with the code below:

// Button.js
import React from 'react';
import { string, bool, func } from 'prop-types';
import { StyledButton } from './styled'

const Button = ({
  size,
  text,
}) => (
  <StyledButton
    size={size}
    // the test will alway fail with result:
    // Expected value to be: "Join us"
    // Received: undefined
    // Unless I add add this line below
    text={text}
  >
    {text} // but the text props is here. That is my current practice of passing the props to the children, am I missing anything?
  </StyledButton>
);

Button.propTypes = {
  size: string,
  text: string,
};

Button.defaultProps = {
  size: '',
  text: '',
};

export default Button;

// Button.test.js
import React from 'react';
import { shallow } from 'enzyme';

import Button from '../../components/Button/Button';

describe('Component: Button', () => {
  const minProps = {
    text: '',
    size: '',
  };  

  it('renders a button in size of "small" with text in it', () => {
    const wrapper = shallow(
      <Button {...minProps} size="small" text="Join us" />
    );

    expect(wrapper.prop('size')).toBe('small');
    expect(wrapper.prop('text')).toBe('Join us');
  });
});


// StyledButton
import Button from 'antd/lib/button';

const StyledButton = styled(Button)`
  &.ant-btn {
    padding: 0 24px;

    ${({ size }) => {
      if (size === 'small') {
        return css`
          font-size: 14px;
          line-height: 32px;
        `;
      }
      return null;
    }};
`;

export { StyledButton };

Does anyone know why the test will not pass unless I pass the props to the StyledButton?

vizFlux
  • 735
  • 2
  • 9
  • 14
  • Related: [retrieve a component's props to test in Jest/Enzyme](https://stackoverflow.com/q/59853548/104380) – vsync Jun 30 '21 at 12:18

2 Answers2

35

You need to find the StyledButton in the Button component before asserting the props

// Button.test.js
import React from 'react';
import { shallow } from 'enzyme';

import Button from '../../components/Button/Button';
import { StyledButton } from './styled'

describe('Component: Button', () => {
  const minProps = {
    text: '',
    size: '',
  };  

  it('renders a button in size of "small" with text in it', () => {
    const wrapper = shallow(
      <Button {...minProps} size="small" text="Join us" />
    );

    expect(wrapper.find(StyledButton).prop('size')).toBe('small');
    expect(wrapper.find(StyledButton).prop('text')).toBe('Join us');
  });
});
vsync
  • 118,978
  • 58
  • 307
  • 400
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
6

I realize this post is a bit old but there's a much better way to test for expected prop types and their values.

Here's what I have and it works fine:

Accordion.js

import React from "react";
import PropTypes from "prop-types";
import { Icon } from "../Icon";
import styled from "styled-components";

const AccordionContainer = styled.div`
  display: flex;
  flex-direction: column;
  flex: 1;
  justify-content: ${props => props.justifyContent};
  background-color: ${props => props.theme.color[props.color]};
  ${props => props.theme.fontSize(14)};
`;

const ChildrenContainer = styled.div`
  display: flex;
  flex-direction: column;
`;

const LabelWrapper = styled.div`
  padding: 10px;
`;

/**
 * Accordion is nearly a Higher Order Component (HOC) in the fact that it encapsulates an Icon and when that
 * Icon is clicked an onClick callback provided should toggle the closed state.
 */
export class Accordion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      closed: props.closed
    };
  }

  render() {
    let {
      props: {
        children,
        hasIcon,
        iconColor,
        iconFlexDirection,
        iconExpand,
        iconName,
        iconSize,
        label,
        color,
        justifyContent
      },
      state: { closed }
    } = this;

    return (
      <AccordionContainer color={color} justifyContent={justifyContent}>
        <div onClick={() => this.setState({ closed: !closed })}>
          {hasIcon ? (
            <>
              <LabelWrapper>
                <Icon
                  fontSize={iconSize}
                  name={iconName}
                  color={iconColor}
                  flexDirection={iconFlexDirection}
                  expand={iconExpand}
                />
              </LabelWrapper>
              {!closed && <ChildrenContainer>{children}</ChildrenContainer>}
            </>
          ) : (
            <>
              <LabelWrapper>
                <div>{label}</div>
              </LabelWrapper>
              {!closed && <ChildrenContainer>{children}</ChildrenContainer>}
            </>
          )}
        </div>
      </AccordionContainer>
    );
  }
}

Accordion.propTypes = {
  color: PropTypes.string,
  closed: PropTypes.bool,
  justifyContent: PropTypes.string,
  hasIcon: PropTypes.bool,
  iconName: PropTypes.string,
  iconColor: PropTypes.string,
  iconExpand: PropTypes.bool,
  iconSize: PropTypes.number,
  label: PropTypes.string
};

Accordion.defaultProps = {
  closed: true,
  hasIcon: false,
  iconExpand: false,
  justifyContent: "flex-start"
};

Accordion.spec.js

import React from "react";
import { shallow, mount, render } from "enzyme";
import styled, { ThemeProvider } from "styled-components";
import theme from "../../styles/theme";
import { Accordion } from "./Accordion";
import sinon from "sinon";

describe("Accordion", () => {

  const AccordionJSX = (
    <ThemeProvider theme={theme}>
      <Accordion
        iconName="home"
        iconColor="#777"
        iconSize={14}
        hasIcon={true}
      >
        HELLO ACCORDION
      </Accordion>
    </ThemeProvider>
  );

  it("Should render without throwing an error", () => {
    expect(shallow(
      AccordionJSX
    )).not.toBeNull();
  });

  const AccordionComponent = mount(AccordionJSX);

  it("Should have a styled-components theme", () => {
    expect(AccordionComponent.props().theme).not.toBeNull();
  });

  it('check props passed in', () => {
    console.log(AccordionComponent.props().children);
    expect(AccordionComponent.props().children.props).toEqual({
      iconName: 'home',
      iconColor: '#777',
      iconSize: 14,
      hasIcon: true,
      children: 'HELLO ACCORDION',
      closed: true,
      iconExpand: false,
      justifyContent: 'flex-start'
    });
  });

  it('check state after opened', () => {
    expect(AccordionComponent.props().theme).not.toBeNull();
  })


});
Jason Rice
  • 446
  • 5
  • 8