0

I created a weather dashboard component in React which consists of a Select Dropdown and based on the values of the Select dropdown, it fetches data, sets states and then renders Charts. Here is the code:

Dashboard.js

function Dashboard() {

  const [label, setLabel] = useState(null) 
  const [labels, setLabels] = useState<String[]>([]) 
  const [data, setData] = useState<Number[]>([])

  const options = [
    { value: 'temperature', label: 'Temperature' },
    { value: 'dewPoint', label: 'Dew Point' },
    { value: 'visibility', label: 'Visibility' }
];

  const handleChange = async (selectedOption: any) => {
    const response = await fetch('http://localhost:3000/getData');
    if (!response.ok) {
        const message = `An error has occured: ${response.status}`;
        throw new Error(message);
    }
    const jsonData = await response.json();
    setLabel(selectedOption.value)
    setLabels(jsonData.map((item: { timestamp: any; }) => item.timestamp))
    if (selectedOption.value === 'temperature') {
      setData(jsonData.map((item: { temperature: any; }) => item.temperature))
    } else if (selectedOption.value === 'dewPoint') {
      setData(jsonData.map((item: { dewPoint: any; }) => item.dewPoint))
    } else {
      setData(jsonData.map((item: { visibility: any; }) => item.visibility))
    }
    
  }

  return (
    <div>
      <div  data-testid = "Select">
      <Select options={options} onChange={handleChange}/>
      </div>
      {label != null && data  != null &&
        <div data-testid= "charts"> 
          <BarChart data-testid = "BarChart" label = {label} labels={labels} selectedData = {data} />
          <RadarChart data-testid = "RadarChart" label = {label} labels={labels} selectedData = {data} />
          <LineChart  data-testid = "LineChart" label = {label} labels={labels} selectedData = {data} />
        </div>
      }
      {
        label == null && data == null &&
        <div data-testid= "no-charts">
          <h1> No data to fetch. </h1>
        </div>
      }
    </div>
    
  );
}

How would I test this component using Jest. I am looking into tutorials and examples, but I come across simple instances where the component being tested just consists of some html elements. Can I use jest.mock() to test this somehow? How would I get it to fetch the data through Jest? Any advice would be appreciated. I feel stuck and I have been at this for hours.

1 Answer 1

1

Generally, you'd use something like React Testing Library which allows you to mount a component inside of jest and then make assertions on it via utilities that inspect the DOM output of that component.

As you mention, you also need to deal with that your component makes network calls. To mock the network call you could use Jests spyOn functionality on window.fetch where you would provide a fake response such that when fetch is called, it returns your fake response rather than actually making the call.

However, this can be cumbersome. I'd usually make use of something like MSW which has a nice interface for faking network calls.

Your example is fairly complicated to assert on as you are rendering charts. Actually, as a first test to write for React, this is at a more advanced level. You could decide to assert on every bar element in the graph but usually, you'd say this is overkill as the chart library you are using is probably also well-tested (unless you've implemented these yourself?), you don't need to test it extensively in your own app.

Additionally, typically charts won't render right inside of Jest anyway as it's a fake DOM environment that doesn't actually fully emulate a browser in terms of layout.

So it might be fine to just say "it gets the right data for the right option and renders the charts with the right data when it arrives". Since you probably don't want to assert on the actual SVG chart elements (and probably can't because of what I said about Jest not being a real browser), you could mock the chart components to basically just output the passed-in data to the DOM, to allow you to assert on it. This is a fairly advanced use case. You'd usually avoid mocking actual components and instead assert on the real dom outputted by the real components in most cases.

I don't know what chart lib you are using so my answer is limited because I can't write the necessary mocks. Let me know though if I can help further.

Additionally, I don't know what select lib you are using so I can't write the bits about selecting the options.

import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { fireEvent, render, screen } from '@testing-library/react'
import Dashboard from './Dashboard' // Change to your file/import

const server = setupServer(
  rest.get('http://localhost:3000/getData', (req, res, ctx) => {
    return res(
        ctx.json([ // I made this up cos I'm not a weather expert but you'd give realistic data
            { temperature: 25, dewPoint: 2, visibility: 10 },
            { temperature: 12, dewPoint: 4, visibility: 9 },
            { temperature: 27, dewPoint: 5, visibility: 4 }
        ])
     )
  }),
)


describe('<Dashboard />', () => {

    beforeAll(() => { 
       // TODO: Mock the chart components with a fake react component that passes through `data` but `JSON.stringify`'ed in the HTML output so you can assert on it.
       server.listen() 
    })
    afterEach(() => server.resetHandlers())
    afterAll(() => server.close())

 
   it('renders temperature charts when temperature option is selected', async () => {
      const { findByTestId } = render(<Dashboard />)
      
      // TODO: Get the option for temperature and use `fireEvent.click` to click it.

      const barChart = await findByTestId('BarChart')
      expect(barChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

      const radarChart = await findByTestId('RadarChart')
      expect(radarChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

      const lineChart = await findByTestId('LineChart  ')
      expect(lineChart.innerHTML).toBe(JSON.stringify([25, 12, 27]))

   })

})

You'd repeat this for the other options.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi thank you for your answer!! I am looking into your solution but to answer your questions, I used Chart.js for the charts and the react-select package: npmjs.com/package/react-select
I am getting an error that says cannot read properties of undefined (reading 'ok'). Is it because of the way the server is set up? How would I fix that?
Hmm thats odd. Could you add a console.log above the return res( and check that it outputs when you run the test or not? One possible thing that is happening is that for some reason, your request actually isnt being captured by MSW for some reason and its trying to make a real request. Above the error you mention, are there more errrors?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.