cypress-io/cypress-react-unit-test

Tests fail when component uses React Hook useHistory

wlsf82 opened this issue · 10 comments

I'm trying to test a react component (https://github.com/wlsf82/serverless-stack-client/blob/main/src/containers/NewNote.js) with cypress-react-unit-test based on https://glebbahmutov.com/blog/my-vision-for-component-tests/#benefits, but my tests keep failing with the following error:

Screenshot 2020-08-17 at 00 52 04

If I comment lines 2, 13, and 43 of my component, then my test suite runs ok.

Here are the tests I wrote:

import React from "react";
import NewNote from "./NewNote";
import { mount } from "cypress-react-unit-test"

describe("NewNote", () => {
  beforeEach(() => mount(<NewNote />));

  context("Form initial state", () => {
    it("Create button is disabled", () => {
      cy.contains("Create").should("be.disabled");
    });
  });

  context("Form already filled", () => {
    it("Create button gets enabled", () => {
      cy.get("#content").type("Some content");

      cy.contains("Create").should("be.enabled");
    });
  });
});

I'm opening this issue since I couldn't find any other related to the use of useHistory's hook.

Any help is appreciated, and please let me know if I can provide any other info.

@bahmutov, would you mind looking into it, please?

Your problem here is that you are missing the context in the tests. Make sure that your test components must be wrapped with all the providers you have in your app.

@wlsf82 can you push your test branch so I can run your test exactly like you have set it up please?

@wlsf82 can you push your test branch so I can run your test exactly like you have set it up please?

Sure, @bahmutov, here it goes https://github.com/wlsf82/serverless-stack-client/blob/component-tests/src/containers/NewNote.spec.js

I tried

<AppContext.Provider value={{ isAuthenticated: true }}>
  <AuthenticatedRoute exact path="/notes/new">
    <NewNote />
  </AuthenticatedRoute>
</AppContext.Provider>

without success, we have to check the top-level local access

I wonder if I did not set the provider correctly here, since similar tests pass in https://github.com/bahmutov/test-react-router-v5

Hi, @bahmutov, I've also just tried an updated version of my test (see below)

import React from "react";
import { AppContext } from "../libs/contextLib";
import AuthenticatedRoute from "../components/AuthenticatedRoute";
import NewNote from "./NewNote";
import { mount } from "cypress-react-unit-test"

describe("NewNote", () => {
  beforeEach(() => {
    mount(
      <AppContext.Provider value={{ isAuthenticated: true, userHasAuthenticated: true }}>
        <AuthenticatedRoute exact path="/notes/new">
          <NewNote />
        </AuthenticatedRoute>
      </AppContext.Provider>,
      {
        stylesheets: [
          "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
        ]
      }
    )
  });

  context("Form initial state", () => {
    it("Create button is disabled", () => {
      cy.contains("Create").should("be.disabled");
    });
  });

  context("Form already filled", () => {
    beforeEach(() => cy.get("#content").type("Some content"));

    it("Create button gets enabled", () => {
      cy.contains("Create").should("be.enabled");
    });

    it("Submits the form", () => {
      cy.contains("Create").click();
    });

    it("Attaches a file", () => {
      const textFile = "../../cypress/fixtures/text.txt";
      cy.get("input[type='file']").attachFile(textFile);
    });

    it("Tries to attaches a large file", () => {
      const textFile = "../../cypress/fixtures/large-file.txt";
      cy.get("input[type='file']").attachFile(textFile);

      cy.contains("Create").click();
    });
  });
});

And now it fails with the following error:

Screenshot 2020-08-18 at 18 20 34

Is that the same you've got?

I've just pushed the code, in case you want to look at it directly on my repo.

Ok, you needed to surround the routes with BrowserRouter component

import { BrowserRouter } from 'react-router-dom'

  beforeEach(() => {
    mount(
      <BrowserRouter>
        <AppContext.Provider value={{ isAuthenticated: true, userHasAuthenticated: true }}>
          <AuthenticatedRoute>
            <NewNote />
          </AuthenticatedRoute>
        </AppContext.Provider>
        </BrowserRouter>,
      {
        stylesheets: [
          "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
        ]
      }
    )
  });

Remember - your tests are like "mini apps", thus the mounted component has to be used like a real thing

Screen Shot 2020-08-21 at 10 32 31 PM

See also https://github.com/bahmutov/test-react-router-v5

note: I removed exact and path=... from <AuthenticatedRoute exact path="/notes/new"> because don't know how to tell the router that the component's location, when mounted from the test, is /notes/new. Will leave it for another day and another issue.