-
Notifications
You must be signed in to change notification settings - Fork 26.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[NEXT-863] Unable to test page components using metadata API with Jest #47299
Comments
Hi, the issue is that I will discuss with the team what we should recommend in this case, but if you want to test a page, you could likely use Playwright or a similar tool to spin up a server where Another solution could be to extract the Page component into a non-page file and import it into Jest from there, and re-export it in your page file. |
I'm having the same issue. Are there any updates on recommendations for testing server components? |
Same issue. |
Also having this issue |
Same issue. |
same issue |
1 similar comment
same issue |
Yup. Same. |
So, is it any way to have tests with new app folder? |
This comment was marked as spam.
This comment was marked as spam.
same issue |
Anybody found a workaround ? |
I am writing the tests as follows: creating a wrapper server component for the Page component and writing Jest tests for it. // app/products/[id]/product-page.tsx
import type { Product } from '@/lib/api/interfaces/product';
interface Props {
product: Product;
}
export default function ProductPage({ product }: Props) {
return (
<div>{product.name}</div>
);
} // app/products/[id]/page.tsx
import ProductPage from './product-page';
interface Params {
params: {
id: string;
};
}
export async function generateMetadata({ params }: Params) {
const product = await fetch('https://api.example.com/...');
const title = product.name;
return { title };
}
export default async function Page({ params }: Params) {
const product = await fetch('https://api.example.com/...');
return <ProductPage product={product} />;
} // __tests__/app/products/[id]/product-page.test.tsx
import { render, screen } from '@testing-library/react';
import ProductPage from '@/app/products/[id]/product-page';
describe('ProductPage', () => {
const product = { id: 1, name: 'product name' };
test('renders props', () => {
render(<ProductPage product={product} />);
expect(screen.getByText(product.name)).toBeInTheDocument();
});
}); |
I was trying to write my first test in a brand new repository and it's a pretty sour taste in the mouth and frustrating when the first test you write blows up and SWC gives you no useful message to go off of. I also don't understand why the arrows for the error messages seem to point to nothing useful or relevant. Am I doing something wrong to get such an ugly useless stack trace, or is this just how SWC works; are people fine with this useless ugliness?¿¿ I love the speed that SWC seems to bring, but if it creates useless errors, it makes me not want it in my tooling if it's providing a bad DX. Minimum ReproductionConfigThis seems to be irrelevant, but here's what I've got going on. I did this so that I could have the test next to the source file/page. /** @type {import('next').NextConfig} */
const nextConfig = {
pageExtensions: ['page.tsx', 'page.ts', 'page.jsx', 'page.js'],
}
module.exports = nextConfig Directory Structure
|
same issue |
Same here, anyone got any ideas? |
I ended up using Cypress to test this, but checking the exported function should be trivial. I know at the end of the day what I'm actually interested in is the outcome of the rendered page so Cypress makes sense. On the other hand that should be part of an implementation that's outside of the scope of my code (it belongs to Next JS, and I'll [blind] trust that the dependency I'm using is doing what I expect it to do). I wasn't originally planning on using Cypress in this project and it added some overhead, but it was nice to give a try since I've been eyeballing it for many years but never tried it out. |
@philwolstenholme unfortunately, #52393 only helps with testing server only code, like the one you may have in libs. |
It's really dissapointing that this bug is still on after 4 months. |
Using RTL for integration testing is very common. Having to use Cypress as a workaround is a real shame |
Same here 👋🏼 |
I'm using this workaround for now. It doesn't look good, but so far it seems to work
|
…nt (#53578) ### 🧐 What's in there? This is another attempt to allow testing server-only code with Jest. ### 🧪 How to test? There's an integration tests which can be triggered with `pnpm testheadless server-only` Here is a more comprehensive setup: <details> <summary><code>app/lib/index.ts</code></summary> ```ts import 'server-only' export function add(num1: number, num2: number) { return num1 + num2 } ``` </details> <details> <summary><code>app/lib/index.test.ts</code></summary> ```ts import { add } from '.' it('adds two numbers', () => { expect(add(1, 3)).toEqual(4) }) ``` </details> <details> <summary><code>app/client-component.tsx</code></summary> ```ts 'use client' import { useState } from 'react' export default function ClientComponent() { const [text, setText] = useState('not clicked yet') return <button onClick={() => setText('clicked!')}>{text}</button> } ``` </details> <details> <summary><code>app/client-component.test.tsx</code></summary> ```ts import { fireEvent, render, screen } from '@testing-library/react' import ClientComponent from './client-component' it('can be clicked', async () => { render(<ClientComponent />) const button = screen.getByRole('button') expect(button).toHaveTextContent('not clicked yet') await fireEvent.click(button) expect(button).toHaveTextContent('clicked!') }) ``` </details> <details> <summary><code>app/server-component.tsx</code></summary> ```ts import { add } from '@/lib' export default function ServerComponent({ a, b }: { a: number; b: number }) { return ( <code role="comment"> {a} + {b} = {add(a, b)} </code> ) } ``` </details> <details> <summary><code>app/server-component.test.tsx</code></summary> ```ts import { render, screen } from '@testing-library/react' import ServerComponent from './server-component' it('renders', () => { render(<ServerComponent a={2} b={3} />) expect(screen.getByRole('comment')).toHaveTextContent('2 + 3 = 5') }) ``` </details> <details> <summary><code>app/page.tsx</code></summary> ```ts import Link from 'next/link' import ClientComponent from './client-component' import ServerComponent from './server-component' export default function Page() { return ( <> <h1>Hello World</h1> <Link href="/dave">Dave?</Link> <p> <ClientComponent /> </p> <p> <ServerComponent a={5} b={2} /> </p> </> ) } ``` </details> <details> <summary><code>app/page.test.tsx</code></summary> ```ts import { render, screen } from '@testing-library/react' import Page from './page' it('greets', () => { render(<Page />) expect(screen.getByRole('link')).toHaveTextContent('Dave?') expect(screen.getByRole('heading')).toHaveTextContent('Hello World') expect(screen.getByRole('button')).toHaveTextContent('not clicked yet') expect(screen.getByRole('comment')).toHaveTextContent('5 + 2 = 7') }) ``` </details> <details> <summary><code>app/[blog]/page.tsx</code></summary> ```ts import { Metadata } from 'next' import Link from 'next/link' type Props = { params: { blog: string } } export async function generateMetadata({ params: { blog: title }, }: Props): Promise<Metadata> { return { title, description: `A blog post about ${title}` } } export default function Page({ params }: Props) { return ( <> <div> <Link href="/">Back</Link> </div> <h1>All about {params.blog}</h1> </> ) } ``` </details> <details> <summary><code>app/[blog]/page.test.tsx</code></summary> ```ts import { render, screen } from '@testing-library/react' import Page from './page' it('has the appropriate title', () => { const title = 'Jane' render(<Page params={{ blog: title }} />) expect(screen.getByRole('heading')).toHaveTextContent(`All about ${title}`) expect(screen.getByRole('link')).toHaveTextContent('Back') }) ``` </details> <details> <summary><code>app/layout.tsx</code></summary> ```ts export default function RootLayout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> ) } ``` </details> <details> <summary><code>jest.config.js</code></summary> ```ts const nextJest = require('next/jest') const createJestConfig = nextJest({ dir: './' }) module.exports = createJestConfig({ testEnvironment: 'jsdom', setupFilesAfterEnv: ['<rootDir>/test-setup.ts'], }) ``` </details> <details> <summary><code>package.json</code></summary> ```ts { "name": "rsc-test", "version": "0.0.0", "private": true, "scripts": { "test": "jest" }, "devDependencies": { "@testing-library/jest-dom": "latest" } } ``` </details> <details> <summary><code>test-setup.ts</code></summary> ```ts import '@testing-library/jest-dom' ``` </details> The app should run and all test should pass. ### ❗ Notes to reviewers #### The problem: 1. next/jest configures jest with a transformer ([jest-transformer](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/swc/jest-transformer.ts)) to compile react code with next -swc 2. the transformers configures next -swc for a given environment: Server or Client, based on jest global environment 3. Based on the environment, next -swc checks for invalid usage of `import('server-only')` `“use client”`, `export const metadata` or `export async function generateMetadata` 4. Because the global test environment is either jsdom or node, the same test suite can not include both client and server components #### Possible mitigations *A. Using jest projects* When configured with [multiple projects](https://jestjs.io/docs/next/configuration/#projects-arraystring--projectconfig), Jest can launch different runners with different environment. This would allow running server-only code in node and client-only code in jsdom. However, it requires user to completely change their jest configuration. It would also require a different setup when scaffolding new app-directory project with create-next. *B. Using doc blocks* Jest allows changing the environment per test file [with docBlock](https://jestjs.io/docs/configuration#testenvironment-string). However, by the time jest is invoking next -swc on a source file to transform it, this information is gone, and next -swc is still invoked with the (wrong) global environment. The PR #52393 provides a workaround for files with `import('server-only')`, but does not allow testing pages with metadata. *C. Always compile for node* Our jest-transformer could always configure next -swc for server: - pass Server-specific validations `import('server-only')` `export const metadata` or `export async function generateMetadata` - does not complain about `"use client"` This is what this PR is about! Fixes #47299 Co-authored-by: Jiachi Liu <4800338+huozhi@users.noreply.github.com>
Same issue, but only occurs on Github Actions; works on my local machine otherwise
|
Did you commit a lockfile update with the patched version of Next? |
i did , but i still have the same issue on my repo -> CraftBeerCares/craftbeercares.github.io#9 |
what version are we meant to be using? |
I 100% agree here. I only considered version 13 for use on client work because the app directory was marked as stable. Being able to write tests isn't something I consider optional, and I have a hard enough time getting other developers to get on board with testing without having to have a workaround for this. The "... just use Playwrite" response is not sufficient. I REALLY like this framework, but it's difficult for me to promote it to my client with this issue unresolved. |
@theboyknowsclass You appear to be using the patched version, not sure why this is still broken. |
This comment was marked as off-topic.
This comment was marked as off-topic.
mine is a client-only app.... no server-side code, seems to be an issue with that? |
it works! thank you so much |
Nextjs meta data makes page test fail as meta data only runs on server and react tests run as client only See vercel/next.js#47299
* feat: add menu to home page * fix: upgrade next for remote test fix Nextjs meta data makes page test fail as meta data only runs on server and react tests run as client only See vercel/next.js#47299
We're also updating examples here: #54989 |
There was an issue that was affecting metadata tests with server side components. Fix linked here: vercel/next.js#47299
* Add CI workflow * Run on all branches with changes to code files * Test commit * Remove test console, add yml file externsion, and remove 16 * Add generation of metadata and re-add 16 * Bump next package to the version fix There was an issue that was affecting metadata tests with server side components. Fix linked here: vercel/next.js#47299 * Add pull request triggers as well * Fix typo & schema --------- Co-authored-by: Brandon Bankert <bankert.brandon@gmail.com>
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
Verify canary release
Provide environment information
Which area(s) of Next.js are affected? (leave empty if unsure)
App directory (appDir: true), Jest (next/jest)
Link to the code that reproduces this issue
https://github.com/andremendonca/metadata-jest-error
To Reproduce
git clone https://github.com/andremendonca/metadata-jest-error.git
npm i
npm test
Describe the Bug
in NextJs 13+ using the experimental App folder, I cannot test any server component that exports
metadata
orgenerateMetadata
from https://beta.nextjs.org/docs/api-reference/metadataResulting in the following error:
Expected Behavior
I expected to be able to test Server Components present on app directory
Which browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
No response
From SyncLinear.com | NEXT-863
The text was updated successfully, but these errors were encountered: