tangway / flag-guessing-game-frontend

0 stars 0 forks source link

set up first tests for React #4

Open tangway opened 6 months ago

tangway commented 6 months ago

tests can be difficult to setup but working on a large scale application with many other people it has these advantages:

tangway commented 6 months ago
tangway commented 6 months ago

ran pnpm install --save-dev @testing-library/react @testing-library/jest-dom jest jest-environment-jsdom @babel/preset-env @babel/preset-react babel-jest

tangway commented 6 months ago

made jest.config.cjs in root folder

module.exports = {
  testEnvironment: 'jsdom',
  verbose: true,
  transform: {
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
tangway commented 6 months ago

made .babelrc in root folder

{
 "presets": ["@babel/preset-env", "@babel/preset-react"]
}
tangway commented 6 months ago

added jest to package.json scripts to run

"scripts": {
    "test": "jest"
  },
tangway commented 6 months ago

added import React from 'react'; to all react components and this test passed

App.test.jsx:

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from '../src/App';
import questions from '../src/questions';

test('should set correct state to true when correct answer is selected', () => {
  const { getByText } = render(<App />);
  const correctAnswerButton = getByText(questions[0].answer);
    // Check if the correct state is now true
  fireEvent.click(correctAnswerButton);
});
tangway commented 6 months ago

realized that the above test does not check for state of correct is set to true and that it's not advisable to check for change via useState.

would need a visual change in the UI instead to test upon

tangway commented 5 months ago

found the right way to test for changed state from RTL recommended tutorial https://www.robinwieruch.de/react-testing-library/ under the chapter "When to use findBy?".

The findBy search variant is used for asynchronous elements which will be there eventually.

wrote up a test:

test('should display CORRECT when correct answer is selected', async () => {
  const { getByText } = render(<App />);
  const correctAnswerButton = getByText(questions[0].answer);
  fireEvent.click(correctAnswerButton);
  expect(await screen.findByText(/CORRECT/)).toBeInTheDocument();
});

problem that got me stuck for awhile: somehow the result of fireEvent.click(correctAnswerButton) made getStatus() display "WRONG" even though it is clicking on the correct answer. it took me some time to realize it was due to the fact that i had placed setCorrectAnswer inside of the checkAnswer function and since setState functions are async it doesnt update immediately and when it checks if (userSelection === correctAnswer) it would not be true

the offending code:

const checkAnswer = userSelection => {
    setCorrectAnswer(questions[0].answer)

    if (userSelection === correctAnswer) {
      setCorrect(true);
    } else {
      setCorrect(false);
    }
  };

so the fix is to declare the state outside of the scope of the function:

const [correctAnswer, setCorrectAnswer] = useState(questions[0].answer);

and with that solved it was easy to write the other test case where the answer is wrong. this is what App.test.jsx looks like now:

import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { test, expect } from 'jest';
import '@testing-library/jest-dom';
import App from '../src/App';
import questions from '../src/questions';

test('should display CORRECT when correct answer is selected', async () => {
  const { getByText } = render(<App />);
  const correctAnswerButton = getByText(questions[0].answer);
  fireEvent.click(correctAnswerButton);
  expect(await screen.findByText(/CORRECT/)).toBeInTheDocument();
});

test('should display WRONG when wrong answer is selected', async () => {
  const { getByText } = render(<App />);
  const correctAnswerButton = getByText('Belize');
  fireEvent.click(correctAnswerButton);
  expect(await screen.findByText(/WRONG/)).toBeInTheDocument();
});