rsnext/test/e2e/link-with-api-rewrite/index.test.ts
Wyatt Johnson 329f6cb9ac
Handle Client Rewrites Correctly (#38340)
When an API route is detected for the `getRouteInfo` method (a route with a `/api` prefix), we should redirect the user to the original destination instead of the rewritten destination. This makes the behaviour consistent with how rewrites have been documented thus far.

The reproduction described in #37783 now causes an invariant violation error (for redirecting to the same URL), but this is instead related to the fact that the router (when rehydrated) attempts to redirect the user again to the correct destination. This should be corrected either in this PR or in a future one that addresses the hydration behaviour directly.

## Bug

- Related to #37783
- Related to #37949

## Example

With the following `next.config.js`:

```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  async rewrites() {
    return {
      beforeFiles: [
        {
          source: '/:path(.*)',
          has: [{ type: 'query', key: 'json', value: 'true' }],
          destination: '/api/json?from=:path',
        },
      ],
    }
  },
}

module.exports = nextConfig
```

The following link:

```jsx
<Link href="/not/real?json=true">
  <a>Take me to JSON</a>
</Link>
```

When clicked, will navigate the user to `/not/real?json=true` and have it's contents served by `/api/json` and not simply redirected to `/api/json` which is incorrect.

Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
2022-07-05 22:44:38 +00:00

74 lines
2.5 KiB
TypeScript

import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { check } from 'next-test-utils'
import { join } from 'path'
import webdriver from 'next-webdriver'
describe('link-with-api-rewrite', () => {
let next: NextInstance
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'app/pages')),
'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')),
},
dependencies: {},
})
})
afterAll(() => next.destroy())
it('should perform hard navigation for rewritten urls', async () => {
const browser = await webdriver(next.url, '/')
try {
// Click the link on the page, we expect that there will be a hard
// navigation later (we do this be checking that the window global is
// unset).
await browser.eval('window.beforeNav = "hi"')
await browser.elementById('rewrite').click()
await check(() => browser.eval('window.beforeNav'), {
test: (content) => content !== 'hi',
})
// Check to see that we were in fact navigated to the correct page.
const pathname = await browser.eval('window.location.pathname')
expect(pathname).toBe('/some/route/for')
// Check to see that the resulting data is coming from the right endpoint.
const text = await browser.eval(
'window.document.documentElement.innerText'
)
expect(text).toBe('{"from":"/some/route/for"}')
} finally {
await browser.close()
}
})
it('should perform hard navigation for direct urls', async () => {
const browser = await webdriver(next.url, '/')
try {
// Click the link on the page, we expect that there will be a hard
// navigation later (we do this be checking that the window global is
// unset).
await browser.eval('window.beforeNav = "hi"')
await browser.elementById('direct').click()
await check(() => browser.eval('window.beforeNav'), {
test: (content) => content !== 'hi',
})
// Check to see that we were in fact navigated to the correct page.
const pathname = await browser.eval('window.location.pathname')
expect(pathname).toBe('/api/json')
// Check to see that the resulting data is coming from the right endpoint.
const text = await browser.eval(
'window.document.documentElement.innerText'
)
expect(text).toBe('{"from":""}')
} finally {
await browser.close()
}
})
})