Next.js Discord

Discord Forum

Next.js Testing Help

Unanswered
Kishu posted this in #help-forum
Open in Discord
KishuOP
Hello,
I have this code in app/page.tsx

<Grid columns={{ xs: '1', md: '2fr 1fr' }} width="auto" gap="5">
        <Box>
          <IssueChart count={count} />
        </Box>
        <Box>
          <LatestIssue />
        </Box>
 </Grid>


Now, I want to run a test with vitest.

In the next.js doc, it is given example for html tag, like <h1>Home</h1>
expect(screen.getByRole('heading', { level: 1, name: 'Home' })).toBeDefined() ...
like that example.

But, Here I have 2 components. Here it is not html code.

How to test this app/page.tsx file ?

9 Replies

Brown bear
The example renders the component with RTL, which only has access to the final rendered HTML as a user would see in a browser.

To test that the IssueChart and LatestIssue components render as expected you would need to select something that those components will render in HTML for example:
function LatestIssue() { return <h2>test</h2> }

// ...
const actualLatestIssue = screen.getByRole('heading', { level: 2 });
expect(actualLatestIssue.textContent).toStrictEqual("test");
KishuOP
@Brown bear , in the app/page.tsx i have those 2 components. suppose, I want to test if thoese components are defined or not ? How to test it then ??
Brown bear
If you are wanting a test like
// screen.getByComponent is a made up magical method
expect(screen.getByComponent(LatestIssue)).toBeInTheDocument();


you are unfortunately SOL. 😭

Their use to be a shallow rendering testing framework, Enzyme, that was able to do this level of testing, but internal politics/funding/sponsorship basically killed the project. I would love for it to be revived or another shallow rendering React testing framework to be implemented because it really helped to create very granular unit tests. I am currently in the process of updating an app from React 16 that has over 1k Enzyme tests to RTL. 🤮
KishuOP
@Brown bear , in the <LatestIssue /> component, I have this code.

<Table.Root>
      <Table.Body>
        {issues.map((issue) => (
          <Table.Row key={issue.id}>
            <Table.Cell>
              <Flex direction="column" align="start" gap="2">
                <Link href={`/issues/${issue.id}`}>{issue.title}</Link>
                <IssueBadge status={issue.status} />
              </Flex>
            </Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>



I am using radix ui. That is why in this page everything is component. How I can test this code then ?
Brown bear
The final html should have a table so you should be able to test for the existance of a table. Something like this should work.
expect(screen.getByRole('table')).toBeInTheDocument();
KishuOP
@Brown bear, after running pnpm test, i got this error in next.js 14.

Error: Uncaught [Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.]

this is LatestesIssue.spec.tsx file,

import { expect, test } from 'vitest'
import { render, screen, within } from '@testing-library/react'
import LatestIssue from '@/app/components/LatestIssues'

test('page', () => {
  const issues = render(<LatestIssue />)
  expect(screen.getByRole('table')).toBeInTheDocument()
})



this is LatestIssue.tsx file,

<Table.Root>
      <Table.Body>
        {issues.map((issue) => (
          <Table.Row key={issue.id}>
            <Table.Cell>
              <Flex direction="column" align="start" gap="2">
                <Link href={`/issues/${issue.id}`}>{issue.title}</Link>
                <IssueBadge status={issue.status} />
              </Flex>
            </Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
    </Table.Root>
@Kishu <@518279229222289418>, after running pnpm test, i got this error in next.js 14. `Error: Uncaught [Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.]` this is `LatestesIssue.spec.tsx` file, import { expect, test } from 'vitest' import { render, screen, within } from '@testing-library/react' import LatestIssue from '@/app/components/LatestIssues' test('page', () => { const issues = render(<LatestIssue />) expect(screen.getByRole('table')).toBeInTheDocument() }) this is `LatestIssue.tsx` file, <Table.Root> <Table.Body> {issues.map((issue) => ( <Table.Row key={issue.id}> <Table.Cell> <Flex direction="column" align="start" gap="2"> <Link href={`/issues/${issue.id}`}>{issue.title}</Link> <IssueBadge status={issue.status} /> </Flex> </Table.Cell> </Table.Row> ))} </Table.Body> </Table.Root>
Brown bear
If your component is rendering multiple <table> elements then you have a few options
- use screen.getAllByRole(...) and count/iterate over the results to validate the component
- use screen.getByRole("table", {options}) where "options" is an object that can narrow down the list of tables. The example from earlier in this thread, when looking at a heading, used { level: 2 } to get only <h2> headings. I am not sure what options would work for the table element if any though.
- select a parent of the table you want and use the within() function to then select the table within(screen.getBy...("element")).getByRole("table") I am not sure if that is the correct syntax I dont have an example handy atm, but based on what you have shared so far this is probably not an option anyway.
KishuOP
personally i think vitest, jest are unnecessary. rather we can directly use playwright or cypress. what do you think ??
Brown bear
They serve different purposes. Playwrite and Cypres are for e2e/integration testing where Jest/Vitest are unit testing frameworks.

The distinction gets a bit fuzzy when you are testing components because both Jest and Vitest rely on other libraries to render jsx/tsx. Sadly the only viable option is to use RTL, which does not support shallow rendering, so you end up testing the raw HTML which is the same content that you would get from testing with Playwrite or Cypress.

However, Jest/Vitest allow you to write more granular tests since you can render a single component. Where, iirc, Playwrite and Cypress act on a live instance of the site, which can make testing some specific conditions rather cumbersome. Another benefit of Jest/Vitest is testing pure JS/TS functions. If you extract logic from the component into pure functions (non-jsx) you can validate them without any restrictions that are imposed by rendering a component with RTL.

I still cry for the days of Enzyme and shallow rendering. 😭