Description
react-testing-library
version:
"react-testing-library": "^2.5.1",react
version:
"react": "^16.3.0",node
version:
v8.9.4npm
(oryarn
) version:
5.6.0
Relevant code or config:
// Test
import sinon from "sinon"
import { Simulate, wait } from "react-testing-library"
import { client } from "../../../_old/graphql/apolloClient"
import { mountApp } from "../../../test/testBehaviors"
jest.mock("../../../_old/graphql/apolloClient")
afterEach(() => {
client.resetStore()
})
describe("Given we load the App", () => {
describe("When data is returned and there is no user ", () => {
describe("And we enter a valid username and password and click submit", () => {
it("Then we call the login mutation with correct arguments and are redirected to the search page", async () => {
const userResolverStub = sinon.stub()
userResolverStub.onFirstCall().returns(null)
userResolverStub.onSecondCall().returns({
id: "fakeId",
email: "fakeEmail",
role: "fakeRole",
firstName: "fakeFirstName",
lastName: "fakeLastName",
algoliaSearchApiKey: "fakeAlgoliaSearchApiKey"
})
const loginMuationMock = sinon.expectation.create("loginMutationMock")
loginMuationMock
.withArgs(sinon.match.any, {
input: { email: "[email protected]", password: "fakePassword" }
})
.once()
.returns({
user: {
id: "fakeId",
email: "fakeEmail",
role: "fakeRole",
firstName: "fakeFirstName",
lastName: "fakeLastName",
algoliaSearchApiKey: "fakeAlgoliaSearchApiKey"
},
company: {
hasPurchasing: true
}
})
const mocks = {
User: userResolverStub,
Mutation: () => ({
// This needed to match the name of the mutation in MutationType.js
login: loginMuationMock
})
}
const { getByTestId } = mountApp({ mocks })
await wait(() => {
const emailTextBox = getByTestId("emailTextBox")
emailTextBox.value = "[email protected]"
Simulate.change(emailTextBox)
})
await wait(() => {
const passwordTextBox = getByTestId("passwordTextBox")
passwordTextBox.value = "fakePassword"
Simulate.change(passwordTextBox)
})
await wait(() => {
const loginForm = getByTestId("loginButton")
Simulate.submit(loginForm)
})
await wait(() => {
loginMuationMock.verify()
expect(getByTestId("search-page")).toBeTruthy()
})
})
})
})
})
// Component
import _ from "lodash"
import React, { Component } from "react"
import { Link, withRouter } from "react-router-dom"
import styled from "styled-components"
import { withLogin } from "../graphql/mutations/loginMutation"
import { ERRORS } from "../../../../errors"
import { Routes } from "../../../../Routes/index"
import Alert from "../../../../sharedComponents/Alert/index"
import BackgroundButton, {
Colors
} from "../../../../sharedComponents/forms/BackgroundButton"
import Input from "../../../../sharedComponents/forms/Input"
import GuestContainer from "../../../../sharedContainers/GuestContainer/index"
const LoginStyled = styled(GuestContainer)`
.password-container {
position: relative;
.forgot-password-link {
position: absolute;
right: 0;
top: 33px;
font-size: 10px;
font-weight: 100;
text-transform: uppercase;
}
}
`
class Login extends Component {
state = {
email: "",
password: ""
}
handleChange = (name, value) => this.setState({ [name]: value })
login = () => {
const { email, password } = this.state
this.props
.mutate({
variables: {
input: {
email,
password: password + 'changed' // <==== trying to break the test
}}
})
.then(() =>
this.props.history.push(
_.get(this.props, "location.state.from", Routes.ROOT)
)
)
.catch(({ graphQLErrors }) => {
switch (graphQLErrors[0].message) {
case ERRORS.WrongEmailOrPassword:
Alert.error("Email or password is incorrect")
break
default:
break
}
})
}
handleSubmit = e => {
e.preventDefault()
this.login()
}
render() {
const { email, password } = this.state
return (
<LoginStyled header="Welcome Back" subHeader="Your kitchen awaits">
<form data-testid="login-form" onSubmit={this.handleSubmit}>
<Input
data-testid="emailTextBox"
value={email}
label="Email"
onChange={e => this.handleChange("email", e.target.value)}
autoFocus
/>
<div className="password-container">
<Input
data-testid="passwordTextBox"
value={password}
type="password"
label="Password"
onChange={e => this.handleChange("password", e.target.value)}
/>
<Link
className="forgot-password-link"
to={`${Routes.RESET}?email=${email}`}
>
Forgot password?
</Link>
</div>
<BackgroundButton
data-testid="loginButton"
noHorizontalPadding
color={Colors.PAPAYA}
type="submit"
disabled={!(email && password)}
>
Login
</BackgroundButton>
</form>
</LoginStyled>
)
}
}
export default withLogin(withRouter(Login))
What you did:
I got the above test working and then I wanted to verify that it would break so I changed the password code like this password: password + 'changed'
so that the test would fail and I could make sure the error was good and all that.
What happened:
The test failed eventually but it took a really long time and then didn't provided useful error feedback. We were instead getting a timeout error.
Reproduction:
I think this reproduces the issue. This seems to run for a really long time and never provide useful error feedback.
https://codesandbox.io/s/rjozql4jnm
Problem description:
We need the tests to fail fast and report back a useful error.
Suggested solution:
wait
should run several times quickly and if it's not able to get a clean run with no errors then it should Promise.reject(error)
with the error
was thrown by the callback function.