Skip to content

Troubleshooting

Flacial edited this page Sep 10, 2022 · 9 revisions

Troubleshooting

Here's a list of the most common errors and how you could fix them

Setup

  • You most likely installed a new module by using npm instead of yarn. While in, theory these tools should be interchangeable, in practice, it can result in such weird errors. Recloning the git repo should help.

  • If you get an error with code: 'ERR_OSSL_EVP_UNSUPPORTED' when running the storybook, run this command: export NODE_OPTIONS=--openssl-legacy-provider on linux/osx, on Powershell: $env:NODE_OPTIONS="--openssl-legacy-provider"

    • This error mostly exist in node v17. Downgrading to v16 could help
  • If you get the following error on Windows:

    title="November 6, 2000 12:00 AM"
    title="November 5, 2000 7:00 PM"
    

    Upgrade node to >= v16.15.1

Testing

Prerequisites

screen is an object that has all the queries to retrieve elements from the DOM.

  • Import it with import { screen } from '@testing-library/react'

Found multiple elements with the text: {TEXT}

This error occurs when multiple elements in the document has the same text. First we'll see the good solution then the BETTER one.

To fix it, you could try screen.getAll*(TEXT) (e.g., screen.getAllByText(TEXT)) then select the index for the element.

To find the element index, run yarn jest but with the DEBUG_PRINT_LIMIT=100000 so it prints all the HTML document. For the HTML document to show, the function must throw an error. Try to use await screen.findByText(TEXT) that will throw an error as there are multiple occurrences of the element then locate the element.

Example:

// Throws error because there are 2 elements with this text
const dropdownBtn = await screen.findByText('Select a module') 
await userEvent.click(dropdownBtn)

Now we run yarn DEBUG_PRINT_LIMIT=100000 jest <TEST_PATH>

When we intentionally throw an error with screen.findByText, it prints this HTML:

image

As we can see, there are two elements with the text Select a module so it throws an error.

We want the second element that has the text Select a module which is the dropdown menu. Therefore, we can update our code to:

// [1] will select the dropdown menu
const dropdownBtn = await screen.findAllByText('Select a module')[1]
await userEvent.click(dropdownBtn)

A better way is to give the component data-testid prop and find it by it:

<DropdownMenu>
  <DropdownToggle data-testid="dropdown-toggle">
    Select a module
  </DropdownToggle>
</DropdownMenu>

Then in the test file:

const dropdownBtn = await screen.findByTestId('dropdown-toggle')
await userEvent.click(dropdownBtn)

Assertion fails when an async code is ran before it

This happens when when the component has an async code that executes when a certain action happen (e.g., click a button).

To fix it, we can use an waitFor or if it's an element we're looking for, we can use screen.find* (e.g, screen.findByText):

findByText

it('should successfully add an exercise', async () => {
    const submitButton = screen.queryByText('Save exercise')
    
    // A mutation is run when the submit button is clicked
    // Therefore, we must wait for it as it is async and we are awaiting it
    // The mutation code: `await addExercise()`
    await userEvent.click(submitButton)

    expect(await screen.findByText('Added the exercise successfully!'))
}

waitFor from '@testing-library/react'

it('should not submit when inputs are empty', async () => {
    const submitButton = await screen.findByText('Save exercise')
    await userEvent.click(submitButton)
    
    // Alt for findAllByText
    await waitFor(() => {
      expect(screen.queryAllByText('Required')[0]).toBeInTheDocument()
    })
}    

Errors occur after updating GetApp query

After updating getApp query, you must update its dummy data in order for the mocked queries to return the data of the query correctly.

If you added the following field to the query, you must add it for each lesson object in the dummy data:

getApp.ts

// ...
modules {
    id      
    name
    content
    order
}
/ ...

lessonData.ts

const dummyLessonsData: Lesson[] = [
  // Some lesson
  {
    // ...
    modules: [
   {
    id: 1,
    name: 'module1',
    content: 'this is module1',
    order: 1,
    author: moduleAuthor,
    lesson: {
      title: 'Foundations of JavaScript',
      order: 0,
      slug: 'js0',
      id: 5,
      description: 'A super simple introduction to help you get started!',
      challenges: []
    }
  },
    ]
    // ...
  }
]

This isn't the most efficient way to create fake data for our queries. Here's one solution

% Funcs column in Jest coverage is low

This happens when some of the functions are not used. Dead functions.

To find these functions, open the HTML page under coverage/lcov-report/index.html and go to the test file.

In our case, it was under /coverage/lcov-report/pages/admin/lessons/[lessonSlug]/[pageName]/index.tsx.html.

Press n to go to the next uncovered line.

In our case, they were these lines from the QueryInfo component:

image

In order for these lines to be covered, they need to be used. To achieve this, we'll write two test cases that closes (dismiss) the alert by clicking the X icon.

it('Should close success alert', async () => {
  expect.assertions(1)

  render(
    <MockedProvider mocks={mocks}>
      <LessonPage />
    </MockedProvider>
  )

  // Used to make the queries resolve
  await act(() => new Promise(res => setTimeout(res, 0)))

  await fillOutIntroductionForms()
  fireEvent.click(screen.getByText('Save changes'))

  await act(() => new Promise(res => setTimeout(res, 0)))

  // The label text for the X icon is "Close alert"
  await userEvent.click(await screen.findByLabelText('Close alert'))

  expect(screen.queryByRole('alert')).not.toBeInTheDocument()
})

it('Should close error alert', async () => {
  expect.assertions(1)

  render(
    <MockedProvider mocks={mocksWithError}>
      <LessonPage />
    </MockedProvider>
  )

  // Used to make the queries resolve
  await act(() => new Promise(res => setTimeout(res, 0)))

  await fillOutIntroductionForms()
  fireEvent.click(screen.getByText('Save changes'))

  await act(() => new Promise(res => setTimeout(res, 0)))

  // The label text for the X icon is "Close alert"
  await userEvent.click(await screen.findByLabelText('Close alert'))

  expect(screen.queryByRole('alert')).not.toBeInTheDocument()
})

By adding these tests that'll make the functions for onDismissData and onDismissError be called, the column % Funcs is now 100%.

The query doesn't remain in loading state

In order for it to remain in the loading state, we must declare the delay property

const updateLessonMutationMockWithLoading = {
  request: { query: UPDATE_LESSON, variables: js1 },
  result: jest.fn(() => ({
    data: {
      updateLesson: js1
    }
  })),
  delay: 100_000_000_000_000 // ~3170 years
}

The delay property prevent the query from resolving for x milliseconds.

Clone this wiki locally