489e65ed98
Fixes https://github.com/vercel/next.js/discussions/32233 ⚠️ If you're looking at this PR please read the complete description including the part about incremental adoption. ## TLDR: Official support for `<Link href="/about">About</Link>` / `<Link href="/about"><CustomComponent /></Link>` / `<Link href="/about"><strong>About</strong></Link>` where `<Link>` always renders `<a>` without edge cases where it doesn’t render `<a>`. You'll no longer have to put an empty `<a>` in `<Link>` with this enabled. ## Full context ### Changes to `<Link>` - Added an `legacyBehavior` prop that defaults to `true` to preserve the defaults we have today, this will allow to run a codemod on existing codebases to move them to the version where `legacyBehavior` becomes `false` by default - When using the new behavior `<Link>` always renders an `<a>` instead of having `React.cloneElement` and passing props onto a child element - When using the new behavior props that can be passed to `<a>` can be passed to `<Link>`. Previously you could do something like `<Link href="/somewhere"><a target="_blank">Download</a></Link>` but with `<Link>` rendering `<a>` it now allows these props to be set on link. E.g. `<Link href="/somewhere" target="_blank"></Link>` / `<Link href="/somewhere" className="link"></Link>` ### Incremental Adoption / Codemod The main reason we haven't made these changes before is that it breaks pretty much all Next.js apps, which is why I've been hesitant to make this change in the past. I've spent a bunch of time figuring out what the right approach is to rolling this out and ended up with an approach that requires existing apps to run a codemod that automatically opts their `<Link>` usage into the old behavior in order to keep the app functioning. This codemod will auto-fix the usage where possible. For example: - When you have `<Link href="/about"><a>About</a></Link>` it'll auto-fix to `<Link href="/about">About</Link>` - When you have `<Link href="/about"><a onClick={() => console.log('clicked')}>About</a></Link>` it'll auto-fix to `<Link href="/about" onClick={() => console.log('clicked')}>About</Link>` - For cases where auto-fixing can't be applied the `legacyBehavior` prop is added. When you have `<Link href="/about"><Component /></Link>` it'll transform to `<Link href="/about" legacyBehavior><Component /></Link>` so that your app keeps functioning using the old behavior for that particular link. It's then up to the dev to move that case out of the `legacyBehavior` prop. **This default will be changed in Next.js 13, it does not affect existing apps in Next.js 12 unless opted in via `experimental.newLinkBehavior` and running the codemod.** Some code samples of what changed: ```jsx const CustomComponent = () => <strong>Hello</strong> // Legacy behavior: `<a>` has to be nested otherwise it's excluded // Renders: <a href="/about">About</a>. `<a>` has to be nested. <Link href="/about"> <a>About</a> </Link> // Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included. <Link href="/about"> <strong>Hello</strong> </Link> // Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included. <Link href="/about"> <CustomComponent /> </Link> // -------------------------------------------------- // New behavior: `<Link>` always renders `<a>` // Renders: <a href="/about">About</a>. `<a>` no longer has to be nested. <Link href="/about"> About </Link> // Renders: <a href="/about"><strong>Hello</strong></a>. `<a>` is included. <Link href="/about"> <strong>Hello</strong> </Link> // Renders: <a href="/about"><strong>Hello</strong></a>. `<a>` is included. <Link href="/about"> <CustomComponent /> </Link> ``` --- ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> |
||
---|---|---|
.. | ||
.stats-app | ||
__mocks__ | ||
development | ||
e2e | ||
integration | ||
lib | ||
production | ||
unit | ||
.gitignore | ||
jest-setup-after-env.ts | ||
readme.md | ||
test-file.txt |
Writing tests for Next.js
Getting Started
You can set-up a new test using yarn new-test
which will start from a template related to the test type.
Test Types in Next.js
- e2e: These tests will run against
next dev
,next start
, and deployed to Vercel - development: These tests only run against
next dev
- production: These tests will run against
next start
. - integration: These tests run misc checks and modes and is where tests used to be added before we added more specific folders. Ideally we don't add new test suites here as tests here are not isolated from the monorepo.
- unit: These are very fast tests that should run without a browser or running next and should be testing a specific utility.
For the e2e, production, and development tests the createNext
utility should be used and an example is available here. This creates an isolated Next.js install to ensure nothing in the monorepo is relied on accidentally causing incorrect tests.
All new test suites should be written in TypeScript either .ts
(or .tsx
for unit tests). This will help ensure we catch smaller issues in tests that could cause flakey or incorrect tests.
If a test suite already exists that relates closely to the item being tested (e.g. hash navigation relates to existing navigation test suites) the new checks can be added in the existing test suite.
Best Practices
- When checking for a condition that might take time, ensure it is waited for either using the browser
waitForElement
or using thecheck
util innext-test-utils
. - When applying a fix, ensure the test fails without the fix. This makes sure the test will properly catch regressions.