jeudi 10 décembre 2020

When I try to test a component that communicates with Firebase, I get a FirebaseError

When I try to test a component that does things like store data in firestore or read data, I get the error FirebaseError: projectId must be a string in FirebaseApp.options.

I'm thinking that this error is probably not a problem with projectId, but I have no idea what is wrong.

The error log looks like this.

 FAIL  src/components/__tests__/Login.test.tsx
  ● Test suite failed to run

    FirebaseError: projectId must be a string in FirebaseApp.options

      14 | });
      15 | 
    > 16 | export const db = firebase.firestore();
         |                            ^
      17 | export const auth = app.auth();
      18 | export const timestamp = firebase.firestore.FieldValue.serverTimestamp();
      19 | export default app;

Here is my test code and the code for the component.

export default function Login() {
  const classes = useStyles();
  const emailRef = useRef();
  const passwordRef = useRef();
  const { login }: any = useAuth();
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const history = useHistory();

  const inputEmail = useCallback(
    (event) => {
      setEmail(event.target.value);
    },
    [setEmail]
  );

  const inputPassword = useCallback(
    (event) => {
      setPassword(event.target.value);
    },
    [setPassword]
  );

  async function handleSubmit(e: any) {
    e.preventDefault();

    setError("");
    setLoading(true);
    return login(email, password)
      .then(() => {
        history.push("/");
      })
      .catch((error: any) => {
        setError("failed!!");
      })
      .finally(() => {
        setLoading(false);
      });
  }

  const onClickGuestButton = () => {
    setError("");
    setLoading(true);
    return login("guest@example.com", "password")
      .then(() => {
        history.push("/");
      })
      .catch((error: any) => {
        setError("failed!!");
      })
      .finally(() => {
        setLoading(false);
      });
  };

  return (
    <>
      {error && <Alert severity="error">{error}</Alert>}
      <Container component="main" maxWidth="xs">
        <div className={classes.paper}>
          <Avatar className={classes.avatar}>
            <LockOutlinedIcon />
          </Avatar>
          <Typography component="h1" variant="h5">
           Login
          </Typography>
          <form className={classes.form} noValidate onSubmit={handleSubmit}>
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              id="email"
              label="Email Address"
              value={email}
              inputRef={emailRef}
              onChange={inputEmail}
            />
            <TextField
              variant="outlined"
              margin="normal"
              required
              fullWidth
              label="Password"
              type="password"
              id="password"
              value={password}
              inputRef={passwordRef}
              onChange={inputPassword}
            />
            <FormControlLabel
              control={<Checkbox value="remember" color="primary" />}
              label="Remember me"
            />
            <Button
              type="submit"
              fullWidth
              variant="contained"
              color="primary"
              className={classes.submit}
              disabled={loading}
            >
              Login
            </Button>
            <Grid container>
              <Grid item xs>
                <Link to="/forgot-password">
                  forget password?
                </Link>
              </Grid>
              <Grid item>
                <Link to="/signup">{"signup"}</Link>
              </Grid>
            </Grid>
          </form>
        </div>
      </Container>
      <Button className={classes.guestLogin} onClick={onClickGuestButton}>
        Login as guest
      </Button>
    </>
  );
}
import * as React from "react";
import Login from "../Login";
import { configure, mount, shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import ReactDOM, * as ReactDom from "react-dom";

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

describe("Login", () => {
  it("renders without crashing", () => {
    console.error = jest.fn();
    const div = document.createElement("div");
    ReactDOM.render(<Login />, div);
    ReactDOM.unmountComponentAtNode(div);
    expect(console.error).not.toHaveBeenCalled();
  });
});

I've heard that it's better to use mock when testing components that communicate with APIs such as firebase, but I'm not sure how to use it.

I also don't know if I should use mock or not in this case.

By the way, of course, if you test the <Login /> component without importing it, the test will pass. Also, not only the component, but also another component will cause a FirebaseError if firebase is used.

Do you have any good ideas?

1 commentaire:

  1. Did you ever find a solution to this? I have the exact same issue.

    RépondreSupprimer