Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
212 lines
6.8 KiB
TypeScript
212 lines
6.8 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import { retry } from 'next-test-utils'
|
|
import type { Request, Response } from 'playwright'
|
|
|
|
describe('app dir - basepath', () => {
|
|
const { next, isNextDev } = nextTestSetup({
|
|
files: __dirname,
|
|
dependencies: {
|
|
sass: 'latest',
|
|
},
|
|
})
|
|
|
|
it('should successfully hard navigate from pages -> app', async () => {
|
|
const browser = await next.browser('/base/pages-path')
|
|
await browser.elementByCss('#to-another').click()
|
|
await browser.waitForElementByCss('#page-2')
|
|
})
|
|
|
|
it('should support `basePath`', async () => {
|
|
const html = await next.render('/base')
|
|
expect(html).toContain('<h1>Test Page</h1>')
|
|
})
|
|
|
|
it('should support Link with basePath prefixed', async () => {
|
|
const browser = await next.browser('/base')
|
|
expect(
|
|
await browser
|
|
.elementByCss('a[href="/base/another"]')
|
|
.click()
|
|
.waitForElementByCss('#page-2')
|
|
.text()
|
|
).toBe(`Page 2`)
|
|
})
|
|
|
|
it('should prefix segment metadata og image with basePath and pathname', async () => {
|
|
const $ = await next.render$('/base/metadata')
|
|
const ogImageHref = $('meta[property="og:image"]').attr('content')
|
|
expect(ogImageHref).toContain('/base/metadata/opengraph-image.png')
|
|
})
|
|
|
|
it('should prefix manifest with basePath', async () => {
|
|
const $ = await next.render$('/base/metadata')
|
|
const manifestHref = $('link[rel="manifest"]').attr('href')
|
|
expect(manifestHref).toContain('/base/manifest.webmanifest')
|
|
})
|
|
|
|
it('should prefix redirect() with basePath', async () => {
|
|
const browser = await next.browser('/base/redirect')
|
|
await retry(async () => {
|
|
expect(await browser.url()).toBe(`${next.url}/base/another`)
|
|
})
|
|
})
|
|
|
|
it('should render usePathname without the basePath', async () => {
|
|
const pathnames = ['/use-pathname', '/use-pathname-another']
|
|
const validatorPromises = pathnames.map(async (pathname) => {
|
|
const $ = await next.render$('/base' + pathname)
|
|
expect($('#pathname').data('pathname')).toBe(pathname)
|
|
})
|
|
await Promise.all(validatorPromises)
|
|
})
|
|
|
|
it('should handle redirect in dynamic in suspense boundary routes with basePath', async () => {
|
|
const browser = await next.browser('/base/dynamic/source')
|
|
await retry(async () => {
|
|
// Check content is loaded first to avoid flakiness
|
|
expect(await browser.elementByCss('p').text()).toBe(`id:dest`)
|
|
expect(await browser.url()).toBe(`${next.url}/base/dynamic/dest`)
|
|
})
|
|
})
|
|
|
|
it.each(['/base/refresh', '/base/refresh?foo=bar'])(
|
|
`should only make a single RSC call to the current page (%s)`,
|
|
async (path) => {
|
|
let rscRequests = []
|
|
const browser = await next.browser(path, {
|
|
beforePageLoad(page) {
|
|
page.on('request', (request) => {
|
|
const headers = request.headers()
|
|
if (
|
|
headers['rsc'] === '1' &&
|
|
// Prefetches also include `rsc`
|
|
headers['next-router-prefetch'] !== '1'
|
|
) {
|
|
rscRequests.push(request.url())
|
|
}
|
|
})
|
|
},
|
|
})
|
|
|
|
await browser.elementByCss('button').click()
|
|
await browser.waitForIdleNetwork()
|
|
await retry(async () => {
|
|
expect(rscRequests).toEqual([
|
|
expect.stringContaining(`${next.url}${path}`),
|
|
])
|
|
})
|
|
}
|
|
)
|
|
|
|
it.each([
|
|
{ redirectType: 'relative', buttonId: 'redirect-relative' },
|
|
{ redirectType: 'absolute', buttonId: 'redirect-absolute-internal' },
|
|
])(
|
|
`should properly stream an internal server action redirect() with a $redirectType URL`,
|
|
async ({ buttonId }) => {
|
|
const initialPagePath = '/base/client'
|
|
const destinationPagePath = '/base/another'
|
|
|
|
const requests: Request[] = []
|
|
const responses: Response[] = []
|
|
|
|
const browser = await next.browser(initialPagePath)
|
|
|
|
browser.on('request', (req) => {
|
|
const url = req.url()
|
|
|
|
if (
|
|
url.includes(initialPagePath) ||
|
|
url.includes(destinationPagePath)
|
|
) {
|
|
requests.push(req)
|
|
}
|
|
})
|
|
|
|
browser.on('response', (res) => {
|
|
const url = res.url()
|
|
|
|
if (
|
|
url.includes(initialPagePath) ||
|
|
url.includes(destinationPagePath)
|
|
) {
|
|
responses.push(res)
|
|
}
|
|
})
|
|
|
|
await browser.elementById(buttonId).click()
|
|
await retry(async () =>
|
|
expect(await browser.url()).toContain('/base/another')
|
|
)
|
|
|
|
expect(await browser.waitForElementByCss('#page-2').text()).toBe(`Page 2`)
|
|
|
|
// This verifies the redirect & server response happens in a single roundtrip,
|
|
// if the redirect resource was static. In development, these responses are always
|
|
// dynamically generated, so we only expect a single request for build/deploy.
|
|
// TODO(client-segment-cache): re-enable when this optimization is added back
|
|
if (!isNextDev && !process.env.__NEXT_CACHE_COMPONENTS) {
|
|
expect(requests).toHaveLength(1)
|
|
expect(responses).toHaveLength(1)
|
|
}
|
|
|
|
const request = requests[0]
|
|
const response = responses[0]
|
|
|
|
expect(request.url()).toEqual(`${next.url}${initialPagePath}`)
|
|
expect(request.method()).toEqual('POST')
|
|
expect(response.status()).toEqual(303)
|
|
}
|
|
)
|
|
|
|
it('should redirect externally when encountering absolute URLs on the same host outside the basePath', async () => {
|
|
const initialPagePath = '/base/client'
|
|
const destinationPagePath = '/outsideBasePath'
|
|
|
|
const requests: Request[] = []
|
|
const responses: Response[] = []
|
|
|
|
const browser = await next.browser(initialPagePath)
|
|
|
|
browser.on('request', (req) => {
|
|
const url = req.url()
|
|
|
|
if (!url.includes('_next')) {
|
|
requests.push(req)
|
|
}
|
|
})
|
|
|
|
browser.on('response', (res) => {
|
|
const url = res.url()
|
|
|
|
if (!url.includes('_next')) {
|
|
responses.push(res)
|
|
}
|
|
})
|
|
|
|
await browser.elementById('redirect-absolute-external').click()
|
|
await retry(async () =>
|
|
expect(await browser.url()).toContain('/outsideBasePath')
|
|
)
|
|
|
|
// We expect to see two requests, first a POST invoking the server
|
|
// action. And second a GET request resolving the redirect.
|
|
expect(requests).toHaveLength(2)
|
|
expect(responses).toHaveLength(2)
|
|
|
|
const [firstRequest, secondRequest] = requests
|
|
const [firstResponse, secondResponse] = responses
|
|
|
|
expect(firstRequest.url()).toEqual(`${next.url}${initialPagePath}`)
|
|
expect(firstRequest.method()).toEqual('POST')
|
|
|
|
expect(secondRequest.url()).toEqual(`${next.url}${destinationPagePath}`)
|
|
expect(secondRequest.method()).toEqual('GET')
|
|
|
|
expect(firstResponse.status()).toEqual(303)
|
|
// Since this is an external request to a resource outside of NextJS
|
|
// we expect to see a separate request resolving the external URL.
|
|
expect(secondResponse.status()).toEqual(200)
|
|
})
|
|
})
|