nfl / react-helmet

A document head manager for React
MIT License
17.36k stars 661 forks source link

[testing] Helmet.peek() returns previous elements in unit testing #460

Open andrastothtw opened 5 years ago

andrastothtw commented 5 years ago

This is a question/bug report.

How can you 'reset' elements put into <head> by Helmet? Helmet.peek() returns elements from previous tests in the same file? See code below:

Code to reproduce

import * as React from 'react';
import { mount } from 'enzyme';
import Helmet from 'react-helmet';

it('affects the other test :O', () => {
  mount(<div>
    <Helmet>
      <link rel="canonical" href="http://lol" />
    </Helmet>
  </div>);

  const helmetContent = Helmet.peek();
  const canonicalLinkTag = (helmetContent as any).linkTags.find((link: any) => link.rel === 'canonical');

  expect(canonicalLinkTag).toHaveProperty('href', 'http://lol');
});

it('finds the previous as being defined', () => {
  mount(<div>
    Wat
  </div>);

  const helmetContent = Helmet.peek();
  const canonicalLinkTag = (helmetContent as any).linkTags.find((link: any) => link.rel === 'canonical');

  expect(canonicalLinkTag).not.toBeDefined();
});

What is the expected behavior? These two cases should be independent.

Which versions of React and react-helmet...? react-helmet: 5.2.1 react: 16.4.1

yes-mam commented 4 years ago

I have the same issue

petetrickey commented 4 years ago

I've managed to get this working when using 'https://testing-library.com/docs/react-testing-library/'

// Home.js

<>
    <Helmet>
        <title>I am a meta title</title>
    </Helmet>
    <h1>I am a title</h1>
</>

// Home.test.js

import { render, wait } from '@testing-library/react'

it('Should render the correct meta data', async () => {
        render(<Home />)
        await wait(() => expect(document.title).toEqual('I am a meta title'))
})
dylanjeffers commented 4 years ago

@andrastothtw I've found the following setup works:

import React from "react"
import {Helmet} from "react-helmet"
import {render} from "enzyme"

Helmet.canUseDOM = false

describe("react-helmet", () => {
  afterEach(() => {
    Helmet.rewind()
  })

  it("test 1", () => {
    render(<MyApp />)
    const helmet = Helmet.peek()
    expect(...).toBeTruthy()
  })

  it("test 2", () => {
    render(<MyApp someProp />)
    const helmet = Helmet.peek()
    expect(...).toBeFalsy()
  })
})

From what I can tell, rewind is the reset function you are looking for, but it doesn't work unless you set Helmet.canUseDOM = false which tells Helmet to render in a server context.

If it helps, the peek and rewind functions come from react-side-effect, which helped me debug this problem.

sandeep609 commented 3 years ago

Any update regarding this issue or any workarounds from other engineers?

Facing the same issue, the order of unit tests within a test class is changing the values I get from Helmet.peek() as Helmet seems to be keeping track of all the mounted components as part of the earlier tests.