rsnext/test/e2e/basepath.test.ts
JJ Kasper c5f1e2f76d
Add test case for /404 client transition (#40734)
This adds a test case verifying client transitions to `/404` or
`/_error` are working properly.

Test deployment with patched reproduction can be seen here
https://router-bug-repro-4bozmev4p-ijjk-testing.vercel.app/blocked

Closes: https://github.com/vercel/next.js/issues/40667
2022-09-21 11:29:52 -07:00

995 lines
34 KiB
TypeScript

import url from 'url'
import { join } from 'path'
import assert from 'assert'
import cheerio from 'cheerio'
import webdriver from 'next-webdriver'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import {
check,
fetchViaHTTP,
hasRedbox,
renderViaHTTP,
waitFor,
} from 'next-test-utils'
describe('basePath', () => {
let next: NextInstance
const basePath = '/docs'
beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'basepath/pages')),
public: new FileRef(join(__dirname, 'basepath/public')),
external: new FileRef(join(__dirname, 'basepath/external')),
},
nextConfig: {
basePath,
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60,
},
async rewrites() {
return [
{
source: '/rewrite-1',
destination: '/gssp',
},
{
source: '/rewrite-no-basepath',
destination: 'https://example.vercel.sh',
basePath: false,
},
{
source: '/rewrite/chain-1',
destination: '/rewrite/chain-2',
},
{
source: '/rewrite/chain-2',
destination: '/gssp',
},
]
},
async redirects() {
return [
{
source: '/redirect-1',
destination: '/somewhere-else',
permanent: false,
},
{
source: '/redirect-no-basepath',
destination: '/another-destination',
permanent: false,
basePath: false,
},
]
},
async headers() {
return [
{
source: '/add-header',
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
{
source: '/add-header-no-basepath',
basePath: false,
headers: [
{
key: 'x-hello',
value: 'world',
},
],
},
]
},
},
})
})
afterAll(() => next.destroy())
const runTests = (isDev = false, isDeploy = false) => {
it('should navigate to /404 correctly client-side', async () => {
const browser = await webdriver(next.url, `${basePath}/slug-1`)
await check(
() => browser.eval('document.documentElement.innerHTML'),
/slug-1/
)
await browser.eval('next.router.push("/404", "/slug-2")')
await check(
() => browser.eval('document.documentElement.innerHTML'),
/page could not be found/
)
expect(await browser.eval('location.pathname')).toBe(`${basePath}/slug-2`)
})
it('should navigate to /_error correctly client-side', async () => {
const browser = await webdriver(next.url, `${basePath}/slug-1`)
await check(
() => browser.eval('document.documentElement.innerHTML'),
/slug-1/
)
await browser.eval('next.router.push("/_error", "/slug-2")')
await check(
() => browser.eval('document.documentElement.innerHTML'),
/page could not be found/
)
expect(await browser.eval('location.pathname')).toBe(`${basePath}/slug-2`)
})
it('should navigate to external site and back', async () => {
const browser = await webdriver(next.url, `${basePath}/external-and-back`)
const initialText = await browser.elementByCss('p').text()
expect(initialText).toBe('server')
await browser
.elementByCss('a')
.click()
.waitForElementByCss('input')
.back()
.waitForElementByCss('p')
await waitFor(1000)
const newText = await browser.elementByCss('p').text()
expect(newText).toBe('server')
})
if (process.env.BROWSER_NAME === 'safari') {
// currently only testing the above test in safari
// we can investigate testing more cases below if desired
return
}
it.each([
{ hash: '#hello?' },
{ hash: '#?' },
{ hash: '##' },
{ hash: '##?' },
{ hash: '##hello?' },
{ hash: '##hello' },
{ hash: '#hello?world' },
{ search: '?hello=world', hash: '#a', query: { hello: 'world' } },
{ search: '?hello', hash: '#a', query: { hello: '' } },
{ search: '?hello=', hash: '#a', query: { hello: '' } },
])(
'should handle query/hash correctly during query updating $hash $search',
async ({ hash, search, query }) => {
const browser = await webdriver(
next.url,
`${basePath}${search || ''}${hash || ''}`
)
await check(
() =>
browser.eval('window.next.router.isReady ? "ready" : "not ready"'),
'ready'
)
expect(await browser.eval('window.location.pathname')).toBe(basePath)
expect(await browser.eval('window.location.search')).toBe(search || '')
expect(await browser.eval('window.location.hash')).toBe(hash || '')
expect(await browser.eval('next.router.pathname')).toBe('/')
expect(
JSON.parse(await browser.eval('JSON.stringify(next.router.query)'))
).toEqual(query || {})
}
)
it('should navigate back correctly to a dynamic route', async () => {
const browser = await webdriver(next.url, `${basePath}`)
expect(await browser.elementByCss('#index-page').text()).toContain(
'index page'
)
await browser.eval('window.beforeNav = 1')
await browser.eval('window.next.router.push("/catchall/first")')
await check(() => browser.elementByCss('p').text(), /first/)
expect(await browser.eval('window.beforeNav')).toBe(1)
await browser.eval('window.next.router.push("/catchall/second")')
await check(() => browser.elementByCss('p').text(), /second/)
expect(await browser.eval('window.beforeNav')).toBe(1)
await browser.eval('window.next.router.back()')
await check(() => browser.elementByCss('p').text(), /first/)
expect(await browser.eval('window.beforeNav')).toBe(1)
await browser.eval('window.history.forward()')
await check(() => browser.elementByCss('p').text(), /second/)
expect(await browser.eval('window.beforeNav')).toBe(1)
})
it('should respect basePath in amphtml link rel', async () => {
const html = await renderViaHTTP(next.url, `${basePath}/amp-hybrid`)
const $ = cheerio.load(html)
const expectedAmpHtmlUrl = isDev
? `${basePath}/amp-hybrid?amp=1`
: `${basePath}/amp-hybrid.amp`
expect($('link[rel=amphtml]').first().attr('href')).toBe(
expectedAmpHtmlUrl
)
})
if (!isDev) {
if (!(global as any).isNextDeploy) {
it('should add basePath to routes-manifest', async () => {
const routesManifest = JSON.parse(
await next.readFile('.next/routes-manifest.json')
)
expect(routesManifest.basePath).toBe(basePath)
})
}
it('should prefetch pages correctly when manually called', async () => {
const browser = await webdriver(next.url, `${basePath}/other-page`)
await browser.eval('window.next.router.prefetch("/gssp")')
await check(
async () => {
const links = await browser.elementsByCss('link[rel=prefetch]')
for (const link of links) {
const href = await link.getAttribute('href')
if (href.includes('gssp')) {
return true
}
}
const scripts = await browser.elementsByCss('script')
for (const script of scripts) {
const src = await script.getAttribute('src')
if (src.includes('gssp')) {
return true
}
}
return false
},
{
test(result) {
return result === true
},
}
)
})
it('should prefetch pages correctly in viewport with <Link>', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.next.router.prefetch("/gssp")')
await check(async () => {
const hrefs = await browser.eval(
`Object.keys(window.next.router.sdc)`
)
hrefs.sort()
assert.deepEqual(
hrefs.map((href) =>
new URL(href).pathname.replace(/\/_next\/data\/[^/]+/, '')
),
[
`${basePath}/gsp.json`,
`${basePath}/index.json`,
// `${basePath}/index/index.json`,
]
)
const prefetches = await browser.eval(
`[].slice.call(document.querySelectorAll("link[rel=prefetch]")).map((e) => new URL(e.href).pathname)`
)
expect(prefetches).toContainEqual(
expect.stringMatching(/\/gsp-[^./]+\.js/)
)
expect(prefetches).toContainEqual(
expect.stringMatching(/\/gssp-[^./]+\.js/)
)
expect(prefetches).toContainEqual(
expect.stringMatching(/\/other-page-[^./]+\.js/)
)
return 'yes'
}, 'yes')
})
}
it('should 404 for public file without basePath', async () => {
const res = await fetchViaHTTP(next.url, '/data.txt')
expect(res.status).toBe(404)
})
it('should serve public file with basePath correctly', async () => {
const res = await fetchViaHTTP(next.url, `${basePath}/data.txt`)
expect(res.status).toBe(200)
expect(await res.text()).toBe('hello world')
})
it('should rewrite with basePath by default', async () => {
const html = await renderViaHTTP(next.url, `${basePath}/rewrite-1`)
expect(html).toContain('getServerSideProps')
})
it('should not rewrite without basePath without disabling', async () => {
const res = await fetchViaHTTP(next.url, '/rewrite-1')
expect(res.status).toBe(404)
})
it('should not rewrite with basePath when set to false', async () => {
// won't 404 as it matches the dynamic [slug] route
const html = await renderViaHTTP(
next.url,
`${basePath}/rewrite-no-basePath`
)
expect(html).toContain('slug')
})
it('should rewrite without basePath when set to false', async () => {
const html = await renderViaHTTP(next.url, '/rewrite-no-basePath')
expect(html).toContain('Example Domain')
})
it('should redirect with basePath by default', async () => {
const res = await fetchViaHTTP(
next.url,
`${basePath}/redirect-1`,
undefined,
{
redirect: 'manual',
}
)
const { pathname } = url.parse(res.headers.get('location') || '')
expect(pathname).toBe(`${basePath}/somewhere-else`)
expect(res.status).toBe(307)
const text = await res.text()
expect(text).toContain(`${basePath}/somewhere-else`)
})
it('should not redirect without basePath without disabling', async () => {
const res = await fetchViaHTTP(next.url, '/redirect-1', undefined, {
redirect: 'manual',
})
expect(res.status).toBe(404)
})
it('should not redirect with basePath when set to false', async () => {
// won't 404 as it matches the dynamic [slug] route
const html = await renderViaHTTP(
next.url,
`${basePath}/rewrite-no-basePath`
)
expect(html).toContain('slug')
})
it('should redirect without basePath when set to false', async () => {
const res = await fetchViaHTTP(
next.url,
'/redirect-no-basepath',
undefined,
{
redirect: 'manual',
}
)
const { pathname } = url.parse(res.headers.get('location') || '')
expect(pathname).toBe('/another-destination')
expect(res.status).toBe(307)
const text = await res.text()
expect(text).toContain('/another-destination')
})
//
it('should add header with basePath by default', async () => {
const res = await fetchViaHTTP(next.url, `${basePath}/add-header`)
expect(res.headers.get('x-hello')).toBe('world')
})
it('should not add header without basePath without disabling', async () => {
const res = await fetchViaHTTP(next.url, '/add-header')
expect(res.headers.get('x-hello')).toBe(null)
})
it('should not add header with basePath when set to false', async () => {
const res = await fetchViaHTTP(
next.url,
`${basePath}/add-header-no-basepath`
)
expect(res.headers.get('x-hello')).toBe(null)
})
it('should add header without basePath when set to false', async () => {
const res = await fetchViaHTTP(next.url, '/add-header-no-basepath')
expect(res.headers.get('x-hello')).toBe('world')
})
it('should not update URL for a 404', async () => {
const browser = await webdriver(next.url, '/missing')
if (isDeploy) {
// the custom 404 only shows inside of the basePath so this
// will be the Vercel default 404 page
expect(
await browser.eval('document.documentElement.innerHTML')
).toContain('NOT_FOUND')
} else {
const pathname = await browser.eval(() => window.location.pathname)
expect(
await browser.eval(() => (window as any).next.router.asPath)
).toBe('/missing')
expect(pathname).toBe('/missing')
}
})
it('should handle 404 urls that start with basePath', async () => {
const browser = await webdriver(next.url, `${basePath}hello`)
if (isDeploy) {
// the custom 404 only shows inside of the basePath so this
// will be the Vercel default 404 page
expect(
await browser.eval('document.documentElement.innerHTML')
).toContain('NOT_FOUND')
} else {
expect(
await browser.eval(() => (window as any).next.router.asPath)
).toBe(`${basePath}hello`)
expect(await browser.eval(() => window.location.pathname)).toBe(
`${basePath}hello`
)
}
})
// TODO: this test has been passing incorrectly since the below check
// wasn't being awaited. We need to investigate if this test is
// correct or not.
it.skip('should navigate back to a non-basepath 404 that starts with basepath', async () => {
const browser = await webdriver(next.url, `${basePath}hello`)
await browser.eval(() => ((window as any).navigationMarker = true))
await browser.eval(() => (window as any).next.router.push('/hello'))
await browser.waitForElementByCss('#pathname')
await browser.back()
await check(
() => browser.eval(() => window.location.pathname),
`${basePath}hello`
)
expect(await browser.eval(() => (window as any).next.router.asPath)).toBe(
`${basePath}hello`
)
expect(await browser.eval(() => (window as any).navigationMarker)).toBe(
true
)
})
it('should update dynamic params after mount correctly', async () => {
const browser = await webdriver(next.url, `${basePath}/hello-dynamic`)
await check(
() => browser.elementByCss('#slug').text(),
/slug: hello-dynamic/
)
})
it('should navigate to index page with getStaticProps', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNavigate = "hi"')
await browser.elementByCss('#index-gsp').click()
await browser.waitForElementByCss('#prop')
expect(await browser.eval('window.beforeNavigate')).toBe('hi')
expect(await browser.elementByCss('#prop').text()).toBe('hello world')
expect(await browser.elementByCss('#nested').text()).toBe('no')
expect(JSON.parse(await browser.elementByCss('#query').text())).toEqual(
{}
)
expect(await browser.elementByCss('#pathname').text()).toBe('/')
if (!isDev) {
const hrefs = await browser.eval(`Object.keys(window.next.router.sdc)`)
hrefs.sort()
expect(
hrefs.map((href) =>
new URL(href).pathname.replace(/\/_next\/data\/[^/]+/, '')
)
).toEqual([
`${basePath}/gsp.json`,
`${basePath}/index.json`,
// `${basePath}/index/index.json`,
])
}
})
// TODO: investigate index/index seems this shouldn't work
// as pages/index.js conflicts with pages/index/index.js
it.skip('should navigate to nested index page with getStaticProps', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNavigate = "hi"')
await browser.elementByCss('#nested-index-gsp').click()
await browser.waitForElementByCss('#prop')
expect(await browser.eval('window.beforeNavigate')).toBe('hi')
expect(await browser.elementByCss('#prop').text()).toBe('hello world')
expect(await browser.elementByCss('#nested').text()).toBe('yes')
expect(JSON.parse(await browser.elementByCss('#query').text())).toEqual(
{}
)
expect(await browser.elementByCss('#pathname').text()).toBe('/index')
if (!isDev) {
const hrefs = await browser.eval(`Object.keys(window.next.router.sdc)`)
hrefs.sort()
expect(
hrefs.map((href) =>
new URL(href).pathname.replace(/\/_next\/data\/[^/]+/, '')
)
).toEqual([
`${basePath}/gsp.json`,
`${basePath}/index.json`,
`${basePath}/index/index.json`,
])
}
})
it('should work with nested folder with same name as basePath', async () => {
const html = await renderViaHTTP(next.url, `${basePath}/docs/another`)
expect(html).toContain('hello from another')
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.next.router.push("/docs/another")')
await check(() => browser.elementByCss('p').text(), /hello from another/)
})
it('should work with normal dynamic page', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.elementByCss('#dynamic-link').click()
await check(
() => browser.eval(() => document.documentElement.innerHTML),
/slug: first/
)
})
it('should work with hash links', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.elementByCss('#hashlink').click()
const url = new URL(await browser.eval(() => window.location.href))
expect(url.pathname).toBe(`${basePath}/hello`)
expect(url.hash).toBe('#hashlink')
})
it('should work with catch-all page', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.elementByCss('#catchall-link').click()
await check(
() => browser.eval(() => document.documentElement.innerHTML),
/parts: hello\/world/
)
})
it('should redirect trailing slash correctly', async () => {
const res = await fetchViaHTTP(
next.url,
`${basePath}/hello/`,
{},
{ redirect: 'manual' }
)
expect(res.status).toBe(308)
const { pathname } = new URL(res.headers.get('location'))
expect(pathname).toBe(`${basePath}/hello`)
const text = await res.text()
expect(text).toContain(`${basePath}/hello`)
})
it('should redirect trailing slash on root correctly', async () => {
const res = await fetchViaHTTP(
next.url,
`${basePath}/`,
{},
{ redirect: 'manual' }
)
expect(res.status).toBe(308)
const { pathname } = new URL(res.headers.get('location'))
expect(pathname).toBe(`${basePath}`)
const text = await res.text()
expect(text).toContain(`${basePath}`)
})
it('should navigate an absolute url', async () => {
const browser = await webdriver(next.url, `${basePath}/absolute-url`)
await browser.waitForElementByCss('#absolute-link').click()
await check(
() => browser.eval(() => window.location.origin),
'https://vercel.com'
)
})
if (!(global as any).isNextDeploy) {
it('should navigate an absolute local url with basePath', async () => {
const browser = await webdriver(
next.url,
`${basePath}/absolute-url-basepath?port=${next.appPort}`
)
await browser.eval('window._didNotNavigate = true')
await browser.waitForElementByCss('#absolute-link').click()
const text = await browser
.waitForElementByCss('#something-else-page')
.text()
expect(text).toBe('something else')
expect(await browser.eval('window._didNotNavigate')).toBe(true)
})
it('should navigate an absolute local url without basePath', async () => {
const browser = await webdriver(
next.url,
`${basePath}/absolute-url-no-basepath?port=${next.appPort}`
)
await browser.waitForElementByCss('#absolute-link').click()
await check(
() => browser.eval(() => location.pathname),
'/rewrite-no-basepath'
)
const text = await browser.elementByCss('body').text()
expect(text).toContain('Example Domain')
})
}
it('should 404 when manually adding basePath with <Link>', async () => {
const browser = await webdriver(
next.url,
`${basePath}/invalid-manual-basepath`
)
await browser.eval('window.beforeNav = "hi"')
await browser.elementByCss('#other-page-link').click()
await check(() => browser.eval('window.beforeNav'), {
test(content) {
return content !== 'hi'
},
})
const html = await browser.eval('document.documentElement.innerHTML')
expect(html).toContain('This page could not be found')
})
it('should 404 when manually adding basePath with router.push', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNav = "hi"')
await browser.eval(`window.next.router.push("${basePath}/other-page")`)
await check(() => browser.eval('window.beforeNav'), {
test(content) {
return content !== 'hi'
},
})
const html = await browser.eval('document.documentElement.innerHTML')
expect(html).toContain('This page could not be found')
})
it('should 404 when manually adding basePath with router.replace', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNav = "hi"')
await browser.eval(`window.next.router.replace("${basePath}/other-page")`)
await check(() => browser.eval('window.beforeNav'), {
test(content) {
return content !== 'hi'
},
})
const html = await browser.eval('document.documentElement.innerHTML')
expect(html).toContain('This page could not be found')
})
it('should show the hello page under the /docs prefix', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
const text = await browser.elementByCss('h1').text()
expect(text).toBe('Hello World')
} finally {
await browser.close()
}
})
it('should have correct router paths on first load of /', async () => {
const browser = await webdriver(next.url, `${basePath}`)
await browser.waitForElementByCss('#pathname')
const pathname = await browser.elementByCss('#pathname').text()
expect(pathname).toBe('/')
const asPath = await browser.elementByCss('#as-path').text()
expect(asPath).toBe('/')
})
it('should have correct router paths on first load of /hello', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.waitForElementByCss('#pathname')
const pathname = await browser.elementByCss('#pathname').text()
expect(pathname).toBe('/hello')
const asPath = await browser.elementByCss('#as-path').text()
expect(asPath).toBe('/hello')
})
it('should fetch data for getStaticProps without reloading', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNavigate = true')
await browser.elementByCss('#gsp-link').click()
await browser.waitForElementByCss('#gsp')
expect(await browser.eval('window.beforeNavigate')).toBe(true)
const props = JSON.parse(await browser.elementByCss('#props').text())
expect(props.hello).toBe('world')
const pathname = await browser.elementByCss('#pathname').text()
expect(pathname).toBe('/gsp')
})
it('should fetch data for getServerSideProps without reloading', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
await browser.eval('window.beforeNavigate = true')
await browser.elementByCss('#gssp-link').click()
await browser.waitForElementByCss('#gssp')
expect(await browser.eval('window.beforeNavigate')).toBe(true)
const props = JSON.parse(await browser.elementByCss('#props').text())
expect(props.hello).toBe('world')
const pathname = await browser.elementByCss('#pathname').text()
const asPath = await browser.elementByCss('#asPath').text()
expect(pathname).toBe('/gssp')
expect(asPath).toBe('/gssp')
})
it('should have correct href for a link', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
const href = await browser.elementByCss('a').getAttribute('href')
const { pathname } = url.parse(href)
expect(pathname).toBe(`${basePath}/other-page`)
})
it('should have correct href for a link to /', async () => {
const browser = await webdriver(next.url, `${basePath}/link-to-root`)
const href = await browser.elementByCss('#link-back').getAttribute('href')
const { pathname } = url.parse(href)
expect(pathname).toBe(`${basePath}`)
})
it('should show 404 for page not under the /docs prefix', async () => {
const text = await renderViaHTTP(next.url, '/hello')
expect(text).not.toContain('Hello World')
expect(text).toContain(
isDeploy ? 'NOT_FOUND' : 'This page could not be found'
)
})
it('should show the other-page page under the /docs prefix', async () => {
const browser = await webdriver(next.url, `${basePath}/other-page`)
try {
const text = await browser.elementByCss('h1').text()
expect(text).toBe('Hello Other')
} finally {
await browser.close()
}
})
it('should have basePath field on Router', async () => {
const html = await renderViaHTTP(next.url, `${basePath}/hello`)
const $ = cheerio.load(html)
expect($('#base-path').text()).toBe(`${basePath}`)
})
it('should navigate to the page without refresh', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
await browser.eval('window.itdidnotrefresh = "hello"')
const text = await browser
.elementByCss('#other-page-link')
.click()
.waitForElementByCss('#other-page-title')
.elementByCss('h1')
.text()
expect(text).toBe('Hello Other')
expect(await browser.eval('window.itdidnotrefresh')).toBe('hello')
} finally {
await browser.close()
}
})
it('should use urls with basepath in router events', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
await check(
() => browser.eval('window.next.router.isReady ? "ready" : "no"'),
'ready'
)
await browser.eval('window._clearEventLog()')
await browser
.elementByCss('#other-page-link')
.click()
.waitForElementByCss('#other-page-title')
const eventLog = await browser.eval('window._getEventLog()')
expect(
eventLog.filter((item) => item[1]?.endsWith('/other-page'))
).toEqual([
['routeChangeStart', `${basePath}/other-page`, { shallow: false }],
['beforeHistoryChange', `${basePath}/other-page`, { shallow: false }],
['routeChangeComplete', `${basePath}/other-page`, { shallow: false }],
])
} finally {
await browser.close()
}
})
it('should use urls with basepath in router events for hash changes', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
await check(
() => browser.eval('window.next.router.isReady ? "ready" : "no"'),
'ready'
)
await browser.eval('window._clearEventLog()')
await browser.elementByCss('#hash-change').click()
const eventLog = await browser.eval('window._getEventLog()')
expect(eventLog).toEqual([
[
'hashChangeStart',
`${basePath}/hello#some-hash`,
{ shallow: false },
],
[
'hashChangeComplete',
`${basePath}/hello#some-hash`,
{ shallow: false },
],
])
} finally {
await browser.close()
}
})
it('should use urls with basepath in router events for cancelled routes', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
await check(
() => browser.eval('window.next.router.isReady ? "ready" : "no"'),
'ready'
)
await browser.eval('window._clearEventLog()')
await browser
.elementByCss('#slow-route')
.click()
.elementByCss('#other-page-link')
.click()
.waitForElementByCss('#other-page-title')
const eventLog = await browser.eval('window._getEventLog()')
expect(eventLog).toEqual([
['routeChangeStart', `${basePath}/slow-route`, { shallow: false }],
[
'routeChangeError',
'Route Cancelled',
true,
`${basePath}/slow-route`,
{ shallow: false },
],
['routeChangeStart', `${basePath}/other-page`, { shallow: false }],
['beforeHistoryChange', `${basePath}/other-page`, { shallow: false }],
['routeChangeComplete', `${basePath}/other-page`, { shallow: false }],
])
} finally {
await browser.close()
}
})
it('should use urls with basepath in router events for failed route change', async () => {
const browser = await webdriver(next.url, `${basePath}/hello`)
try {
await check(
() => browser.eval('window.next.router.isReady ? "ready" : "no"'),
'ready'
)
await browser.eval('window._clearEventLog()')
await browser.elementByCss('#error-route').click()
await check(async () => {
const eventLog = await browser.eval('window._getEventLog()')
assert.deepEqual(eventLog, [
['routeChangeStart', `${basePath}/error-route`, { shallow: false }],
[
'routeChangeError',
'Failed to load static props',
null,
`${basePath}/error-route`,
{ shallow: false },
],
])
return 'success'
}, 'success')
} finally {
await browser.close()
}
})
it('should allow URL query strings without refresh', async () => {
const browser = await webdriver(next.url, `${basePath}/hello?query=true`)
try {
await browser.eval('window.itdidnotrefresh = "hello"')
await new Promise((resolve, reject) => {
// Timeout of EventSource created in setupPing()
// (on-demand-entries-utils.js) is 5000 ms (see #13132, #13560)
setTimeout(resolve, isDev ? 10000 : 1000)
})
expect(await browser.eval('window.itdidnotrefresh')).toBe('hello')
const pathname = await browser.elementByCss('#pathname').text()
expect(pathname).toBe('/hello')
expect(await browser.eval('window.location.pathname')).toBe(
`${basePath}/hello`
)
expect(await browser.eval('window.location.search')).toBe('?query=true')
if (isDev) {
expect(await hasRedbox(browser, false)).toBe(false)
}
} finally {
await browser.close()
}
})
it('should allow URL query strings on index without refresh', async () => {
const browser = await webdriver(next.url, `${basePath}?query=true`)
try {
await browser.eval('window.itdidnotrefresh = "hello"')
await new Promise((resolve, reject) => {
// Timeout of EventSource created in setupPing()
// (on-demand-entries-utils.js) is 5000 ms (see #13132, #13560)
setTimeout(resolve, isDev ? 10000 : 1000)
})
expect(await browser.eval('window.itdidnotrefresh')).toBe('hello')
const pathname = await browser.elementByCss('#pathname').text()
expect(pathname).toBe('/')
expect(await browser.eval('window.location.pathname')).toBe(basePath)
expect(await browser.eval('window.location.search')).toBe('?query=true')
if (isDev) {
expect(await hasRedbox(browser, false)).toBe(false)
}
} finally {
await browser.close()
}
})
it('should correctly replace state when same asPath but different url', async () => {
const browser = await webdriver(next.url, `${basePath}`)
try {
await browser.elementByCss('#hello-link').click()
await browser.waitForElementByCss('#something-else-link')
await browser.elementByCss('#something-else-link').click()
await browser.waitForElementByCss('#something-else-page')
await browser.back()
await browser.waitForElementByCss('#index-page')
await browser.forward()
await browser.waitForElementByCss('#something-else-page')
} finally {
await browser.close()
}
})
}
runTests((global as any).isNextDev, (global as any).isNextDeploy)
})