Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forbidden e2e tests #3

Open
wants to merge 3 commits into
base: elef/introduce-error-builder
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { forbidden } from 'next/navigation'

// avoid static generation to fill the dynamic params
export const dynamic = 'force-dynamic'

export default function Page({ params: { id } }) {
if (id === '403') {
forbidden()
}

return <p id="page">{`dynamic-layout-without-forbidden [id]`}</p>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function Layout({ children }) {
return (
<div>
<h1>Dynamic with Layout</h1>
{children}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <div>dynamic-with-layout</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Forbidden() {
return <div id="forbidden">{`dynamic/[id] forbidden`}</div>
}
12 changes: 12 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/dynamic/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { forbidden } from 'next/navigation'

// avoid static generation to fill the dynamic params
export const dynamic = 'force-dynamic'

export default function Page({ params: { id } }) {
if (id === '403') {
forbidden()
}

return <p id="page">{`dynamic [id]`}</p>
}
3 changes: 3 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/dynamic/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Test() {
return <div>dynamic</div>
}
4 changes: 4 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/error-boundary/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use client'
export default function Error() {
return <div>There was an error</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { forbidden } from 'next/navigation'

export default function Page({ params }) {
if (params.dynamic === 'trigger-forbidden') {
forbidden()
}

return <div>Hello World</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <h1>Forbidden (error-boundary/nested)</h1>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use client'
export default function Error() {
return <div>There was an error</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client'
import { forbidden } from 'next/navigation'
import React from 'react'
export default function Page() {
const [shouldError, setShouldError] = React.useState(false)
if (shouldError) {
forbidden()
}
return (
<button
onClick={() => {
setShouldError(true)
}}
>
Trigger Forbidden
</button>
)
}
18 changes: 18 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/error-boundary/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client'
import { forbidden } from 'next/navigation'
import React from 'react'
export default function Page() {
const [shouldError, setShouldError] = React.useState(false)
if (shouldError) {
forbidden()
}
return (
<button
onClick={() => {
setShouldError(true)
}}
>
Trigger Forbidden
</button>
)
}
11 changes: 11 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/forbidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function Forbidden() {
return (
<>
<h1>Root Forbidden</h1>

<div id="timestamp">{Date.now()}</div>
</>
)
}

Forbidden.displayName = 'Forbidden'
16 changes: 16 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function Layout({ children }) {
return (
<html>
<head />
<body>
<header>
<nav id="layout-nav">Navbar</nav>
</header>
<main>{children}</main>
<footer>
<p id="layout-footer">Footer</p>
</footer>
</body>
</html>
)
}
11 changes: 11 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/not-found.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function NotFound() {
return (
<>
<h1>Root Not Found</h1>

<div id="timestamp">{Date.now()}</div>
</>
)
}

NotFound.displayName = 'NotFound'
3 changes: 3 additions & 0 deletions test/e2e/app-dir/forbidden/basic/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <h1>My page</h1>
}
129 changes: 129 additions & 0 deletions test/e2e/app-dir/forbidden/basic/forbidden-basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { nextTestSetup } from 'e2e-utils'

describe('forbidden-basic', () => {
const { next, isNextStart, isNextDev } = nextTestSetup({
files: __dirname,
skipDeployment: true,
})

it("should propagate forbidden errors past a segment's error boundary", async () => {
let browser = await next.browser('/error-boundary')
await browser.elementByCss('button').click()
expect(await browser.elementByCss('h1').text()).toBe('Root Forbidden')

browser = await next.browser('/error-boundary/nested/nested-2')
await browser.elementByCss('button').click()
expect(await browser.elementByCss('h1').text()).toBe(
'Forbidden (error-boundary/nested)'
)

browser = await next.browser('/error-boundary/nested/trigger-forbidden')
expect(await browser.elementByCss('h1').text()).toBe(
'Forbidden (error-boundary/nested)'
)
})

if (isNextStart) {
// TODO(@panteliselef): Should i implement this
it.skip('should include not found client reference manifest in the file trace', async () => {
const fileTrace = JSON.parse(
await next.readFile('.next/server/app/_not-found/page.js.nft.json')
)

const isTraced = fileTrace.files.some((filePath) =>
filePath.includes('page_client-reference-manifest.js')
)

expect(isTraced).toBe(true)
})

it.skip('should not output /404 in tree view logs', async () => {
const output = await next.cliOutput
expect(output).not.toContain('○ /404')
})

it.skip('should use root not-found content for 404 html', async () => {
// static /404 page will use /_not-found content
const page404 = await next.readFile('.next/server/pages/404.html')
expect(page404).toContain('Root Forbidden')
})
}

const runTests = ({ isEdge }: { isEdge: boolean }) => {
it('should match dynamic route forbidden boundary correctly', async () => {
// `/dynamic` display works
const browserDynamic = await next.browser('/dynamic')
expect(await browserDynamic.elementByCss('main').text()).toBe('dynamic')

// `/dynamic/404` calling notFound() will match the same level not-found boundary
const browserDynamic404 = await next.browser('/dynamic/403')
expect(await browserDynamic404.elementByCss('#forbidden').text()).toBe(
'dynamic/[id] forbidden'
)

const browserDynamicId = await next.browser('/dynamic/123')
expect(await browserDynamicId.elementByCss('#page').text()).toBe(
'dynamic [id]'
)
})

it('should escalate forbidden to parent layout if no forbidden boundary present in current layer', async () => {
const browserDynamic = await next.browser(
'/dynamic-layout-without-forbidden'
)
expect(await browserDynamic.elementByCss('h1').text()).toBe(
'Dynamic with Layout'
)

// no forbidden boundary in /dynamic-layout-without-not-found, escalate to parent layout to render root forbidden
const browserDynamicId = await next.browser(
'/dynamic-layout-without-forbidden/403'
)
expect(await browserDynamicId.elementByCss('h1').text()).toBe(
'Root Forbidden'
)

const browserDynamic404 = await next.browser(
'/dynamic-layout-without-forbidden/123'
)
expect(await browserDynamic404.elementByCss('#page').text()).toBe(
'dynamic-layout-without-forbidden [id]'
)
})

// TODO(@panteliselef): Do we need a 401.html ?
if (!isNextDev && !isEdge) {
it('should create the 404 mapping and copy the file to pages', async () => {
const html = await next.readFile('.next/server/pages/404.html')
expect(html).toContain('Root Forbidden')
expect(
await next.readFile('.next/server/pages-manifest.json')
).toContain('"pages/404.html"')
})
}
}

describe('with default runtime', () => {
runTests({ isEdge: false })
})

describe('with runtime = edge', () => {
let originalLayout = ''
const FILE_PATH = 'app/layout.tsx'

beforeAll(async () => {
await next.stop()
originalLayout = await next.readFile(FILE_PATH)
await next.patchFile(
FILE_PATH,
`export const runtime = 'edge'\n${originalLayout}`
)
await next.start()
})
afterAll(async () => {
await next.patchFile(FILE_PATH, originalLayout)
})

runTests({ isEdge: true })
})
})
6 changes: 6 additions & 0 deletions test/e2e/app-dir/forbidden/basic/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {}

module.exports = nextConfig
11 changes: 11 additions & 0 deletions test/e2e/app-dir/forbidden/conflict-route/app/forbidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function Forbidden() {
return (
<>
<h1>This Is The Forbidden Page</h1>

<div id="timestamp">{Date.now()}</div>
</>
)
}

Forbidden.displayName = 'Forbidden'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function notFound() {
return <h1>custom forbidden</h1>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <h1>I'm still a valid page</h1>
}
16 changes: 16 additions & 0 deletions test/e2e/app-dir/forbidden/conflict-route/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default function Layout({ children }) {
return (
<html>
<head />
<body>
<header>
<nav id="layout-nav">Navbar</nav>
</header>
<main>{children}</main>
<footer>
<p id="layout-footer">Footer</p>
</footer>
</body>
</html>
)
}
3 changes: 3 additions & 0 deletions test/e2e/app-dir/forbidden/conflict-route/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <h1>My page</h1>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { nextTestSetup } from 'e2e-utils'

describe('forbidden-conflict', () => {
const { next } = nextTestSetup({
files: __dirname,
skipDeployment: true,
})

const runTests = () => {
it('should allow to have a valid /forbidden route', async () => {
const html = await next.render('/forbidden')
expect(html).toContain("I'm still a valid page")
})
}

describe('with default runtime', () => {
runTests()
})

describe('with runtime = edge', () => {
let originalLayout = ''
const FILE_PATH = 'app/layout.tsx'

beforeAll(async () => {
await next.stop()
originalLayout = await next.readFile(FILE_PATH)
await next.patchFile(
FILE_PATH,
`export const runtime = 'edge'\n${originalLayout}`
)
await next.start()
})
afterAll(async () => {
await next.patchFile(FILE_PATH, originalLayout)
})

runTests()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { forbidden } from 'next/navigation'

export const dynamic = 'force-dynamic'

export default function Page({ params }) {
if (params.id === '403') {
forbidden()
}

return <p>{`group-dynamic [id]`}</p>
}
3 changes: 3 additions & 0 deletions test/e2e/app-dir/forbidden/group-route-root/app/forbidden.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Forbidden() {
return <p>Forbidden placeholder</p>
}
11 changes: 11 additions & 0 deletions test/e2e/app-dir/forbidden/group-route-root/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function RootLayout({ children }) {
return (
<html>
<head />
<body>
<h1>Root layout</h1>
{children}
</body>
</html>
)
}
Loading
Loading