Skip to content

Commit 0de1c16

Browse files
committed
chore(CI): Split trailing-slash suite cases into smaller suites, port to more modern e2e framework
1 parent fcf40f6 commit 0de1c16

14 files changed

+332
-369
lines changed
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-env jest */
2+
3+
import {
4+
testShouldRedirect,
5+
testLinkShouldRewriteTo,
6+
} from './shared-tests.util'
7+
import { nextTestSetup, isNextDev } from 'e2e-utils'
8+
9+
// we don't need to exhaustively test this, just do some basic sanity checks
10+
// for use in combiantion with basePath
11+
;(isNextDev ? describe : describe.skip)(
12+
'Trailing slashes in development mode, with basepath, trailingSlash: true',
13+
() => {
14+
const { next } = nextTestSetup({
15+
files: __dirname,
16+
nextConfig: {
17+
trailingSlash: true,
18+
basePath: '/docs',
19+
},
20+
dependencies: {
21+
'@next/third-parties': 'canary',
22+
},
23+
})
24+
25+
testShouldRedirect(next, [
26+
['/docs/about', '/docs/about/'],
27+
['/docs', '/docs/'],
28+
['/docs/catch-all/hello/world', '/docs/catch-all/hello/world/'],
29+
['/docs/catch-all/hello.world/', '/docs/catch-all/hello.world'],
30+
])
31+
32+
testLinkShouldRewriteTo(next, [
33+
['/docs/linker?href=/about', '/docs/about/'],
34+
['/docs/linker?href=/', '/docs/'],
35+
])
36+
}
37+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/* eslint-env jest */
2+
3+
// These tests are defined here and used in `app-dir.test.ts` and
4+
// `pages-dir.test.ts` so that both test suites can be run in parallel.
5+
6+
import type { Playwright } from 'next-webdriver'
7+
8+
import cheerio from 'cheerio'
9+
import type { NextInstance } from 'e2e-utils'
10+
11+
export function testShouldRedirect(
12+
next: NextInstance,
13+
expectations: [string, string][]
14+
) {
15+
it.each(expectations)(
16+
'%s should redirect to %s',
17+
async (route, expectedLocation) => {
18+
const res = await next.fetch(route, { redirect: 'manual' })
19+
expect(res.status).toBe(308)
20+
const { pathname } = new URL(res.headers.get('location'))
21+
expect(pathname).toBe(expectedLocation)
22+
}
23+
)
24+
}
25+
26+
export function testShouldResolve(
27+
next: NextInstance,
28+
expectations: [string, string, string][]
29+
) {
30+
it.each(expectations)(
31+
'%s should resolve to %s, with router path %s',
32+
async (route, expectedPage, expectedRouterPath) => {
33+
const res = await next.fetch(route, { redirect: 'error' })
34+
expect(res.status).toBe(200)
35+
const $ = cheerio.load(await res.text())
36+
expect($('#page-marker').text()).toBe(expectedPage)
37+
expect($('#router-pathname').text()).toBe(expectedRouterPath)
38+
}
39+
)
40+
41+
it.each(expectations)(
42+
'%s should client side render %s, with router path %s',
43+
async (route, expectedPage, expectedRouterPath) => {
44+
let browser: Playwright | undefined
45+
try {
46+
browser = await next.browser(route)
47+
48+
await browser.waitForElementByCss('#hydration-marker')
49+
const text = await browser.elementByCss('#page-marker').text()
50+
expect(text).toBe(expectedPage)
51+
const routerPathname = await browser
52+
.elementByCss('#router-pathname')
53+
.text()
54+
expect(routerPathname).toBe(expectedRouterPath)
55+
} finally {
56+
if (browser) await browser.close()
57+
}
58+
}
59+
)
60+
}
61+
62+
export function testExternalLinkShouldRewriteTo(
63+
next: NextInstance,
64+
expectations: [string, string][]
65+
) {
66+
it.each(expectations)(
67+
'%s should have href %s',
68+
async (linkPage, expectedHref) => {
69+
const $ = await next.render$(linkPage)
70+
expect($('#link').attr('href')).toBe(expectedHref)
71+
}
72+
)
73+
}
74+
75+
export function testLinkShouldRewriteTo(
76+
next: NextInstance,
77+
expectations: [string, string][]
78+
) {
79+
it.each(expectations)(
80+
'%s should have href %s',
81+
async (linkPage, expectedHref) => {
82+
const $ = await next.render$(linkPage)
83+
expect($('#link').attr('href')).toBe(expectedHref)
84+
}
85+
)
86+
87+
it.each(expectations)(
88+
'%s should navigate to %s',
89+
async (linkPage, expectedHref) => {
90+
let browser: Playwright | undefined
91+
try {
92+
browser = await next.browser(linkPage)
93+
await browser.elementByCss('#link').click()
94+
95+
await browser.waitForElementByCss('#hydration-marker')
96+
const url = new URL(await browser.eval('window.location.href'))
97+
const pathname = url.href.slice(url.origin.length)
98+
expect(pathname).toBe(expectedHref)
99+
} finally {
100+
if (browser) await browser.close()
101+
}
102+
}
103+
)
104+
105+
it.each(expectations)(
106+
'%s should push route to %s',
107+
async (linkPage, expectedHref) => {
108+
let browser: Playwright | undefined
109+
try {
110+
browser = await next.browser(linkPage)
111+
await browser.elementByCss('#route-pusher').click()
112+
113+
await browser.waitForElementByCss('#hydration-marker')
114+
const url = new URL(await browser.eval('window.location.href'))
115+
const pathname = url.href.slice(url.origin.length)
116+
expect(pathname).toBe(expectedHref)
117+
} finally {
118+
if (browser) await browser.close()
119+
}
120+
}
121+
)
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/* eslint-env jest */
2+
/* eslint-disable jest/no-standalone-expect */
3+
4+
import {
5+
testShouldRedirect,
6+
testLinkShouldRewriteTo,
7+
testShouldResolve,
8+
testExternalLinkShouldRewriteTo,
9+
} from './shared-tests.util'
10+
import { nextTestSetup } from 'e2e-utils'
11+
import { join } from 'path'
12+
13+
describe('Trailing slashes with trailingSlash: true', () => {
14+
const { next, isNextDev } = nextTestSetup({
15+
files: __dirname,
16+
nextConfig: {
17+
trailingSlash: true,
18+
},
19+
dependencies: {
20+
'@next/third-parties': 'canary',
21+
},
22+
})
23+
24+
testShouldRedirect(next, [
25+
['/about', '/about/'],
26+
['/catch-all/hello/world', '/catch-all/hello/world/'],
27+
['/catch-all/hello.world/', '/catch-all/hello.world'],
28+
])
29+
30+
testShouldResolve(next, [
31+
// visited url, expected page, expected router path
32+
['/', '/index.js', '/'],
33+
['/about/', '/about.js', '/about'],
34+
[
35+
'/catch-all/hello/world/',
36+
'/catch-all/[...slug].js',
37+
'/catch-all/[...slug]',
38+
],
39+
['/about/?hello=world', '/about.js', '/about'],
40+
])
41+
42+
testLinkShouldRewriteTo(next, [
43+
['/linker?href=/', '/'],
44+
['/linker?href=/about', '/about/'],
45+
['/linker?href=/about/', '/about/'],
46+
['/linker?href=/about?hello=world', '/about/?hello=world'],
47+
['/linker?href=/about/?hello=world', '/about/?hello=world'],
48+
['/linker?href=/catch-all/hello/', '/catch-all/hello/'],
49+
['/linker?href=/catch-all/hello.world/', '/catch-all/hello.world'],
50+
])
51+
52+
testExternalLinkShouldRewriteTo(next, [
53+
[
54+
`/external-linker?href=${encodeURI('https://nextjs.org')}`,
55+
'https://nextjs.org',
56+
],
57+
[
58+
`/external-linker?href=${encodeURI('https://nextjs.org/')}`,
59+
'https://nextjs.org/',
60+
],
61+
])
62+
63+
// only prod builds have a manifest
64+
;(isNextDev ? it.skip : it)(
65+
'should have a trailing redirect in the routesmanifest',
66+
async () => {
67+
const manifest = await next.readJSON(
68+
join('.next', 'routes-manifest.json')
69+
)
70+
expect(manifest).toEqual(
71+
expect.objectContaining({
72+
redirects: expect.arrayContaining([
73+
expect.objectContaining({
74+
source:
75+
'/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/',
76+
destination: '/:file',
77+
statusCode: 308,
78+
}),
79+
expect.objectContaining({
80+
source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)',
81+
destination: '/:notfile/',
82+
statusCode: 308,
83+
}),
84+
]),
85+
})
86+
)
87+
}
88+
)
89+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/* eslint-env jest */
2+
/* eslint-disable jest/no-standalone-expect */
3+
4+
import {
5+
testShouldRedirect,
6+
testLinkShouldRewriteTo,
7+
testShouldResolve,
8+
testExternalLinkShouldRewriteTo,
9+
} from './shared-tests.util'
10+
import { nextTestSetup } from 'e2e-utils'
11+
import { join } from 'path'
12+
13+
describe('Trailing slashes with trailingSlash: false', () => {
14+
const { next, isNextDev } = nextTestSetup({
15+
files: __dirname,
16+
nextConfig: {
17+
trailingSlash: false,
18+
},
19+
dependencies: {
20+
'@next/third-parties': 'canary',
21+
},
22+
})
23+
24+
testShouldRedirect(next, [
25+
['/about/', '/about'],
26+
['/catch-all/hello/world/', '/catch-all/hello/world'],
27+
['/catch-all/hello.world/', '/catch-all/hello.world'],
28+
])
29+
30+
testShouldResolve(next, [
31+
// visited url, expected page, expected router path
32+
['/', '/index.js', '/'],
33+
['/about', '/about.js', '/about'],
34+
[
35+
'/catch-all/hello/world',
36+
'/catch-all/[...slug].js',
37+
'/catch-all/[...slug]',
38+
],
39+
['/about?hello=world', '/about.js', '/about'],
40+
])
41+
42+
testLinkShouldRewriteTo(next, [
43+
['/linker?href=/', '/'],
44+
['/linker?href=/about', '/about'],
45+
['/linker?href=/about/', '/about'],
46+
['/linker?href=/about?hello=world', '/about?hello=world'],
47+
['/linker?href=/about/?hello=world', '/about?hello=world'],
48+
['/linker?href=/catch-all/hello/', '/catch-all/hello'],
49+
['/linker?href=/catch-all/hello.world/', '/catch-all/hello.world'],
50+
])
51+
52+
testExternalLinkShouldRewriteTo(next, [
53+
[
54+
`/external-linker?href=${encodeURI('https://nextjs.org')}`,
55+
'https://nextjs.org',
56+
],
57+
[
58+
`/external-linker?href=${encodeURI('https://nextjs.org/')}`,
59+
'https://nextjs.org/',
60+
],
61+
])
62+
63+
// only prod builds have a manifest
64+
;(isNextDev ? it.skip : it)(
65+
'should have a redirect in the routesmanifest',
66+
async () => {
67+
const manifest = await next.readJSON(
68+
join('.next', 'routes-manifest.json')
69+
)
70+
expect(manifest).toEqual(
71+
expect.objectContaining({
72+
redirects: expect.arrayContaining([
73+
expect.objectContaining({
74+
source: '/:path+/',
75+
destination: '/:path+',
76+
statusCode: 308,
77+
}),
78+
]),
79+
})
80+
)
81+
}
82+
)
83+
})

test/integration/trailing-slashes/next.config.js

-4
This file was deleted.

0 commit comments

Comments
 (0)