Skip to content

Commit 8a43e92

Browse files
committed
type tests
1 parent 7e7fa4d commit 8a43e92

File tree

7 files changed

+102
-6
lines changed

7 files changed

+102
-6
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type Expect<T extends true> = T
2+
export type Equals<A, B> =
3+
(<G>() => G extends A ? 1 : 2) extends <G>() => G extends B ? 1 : 2
4+
? true
5+
: false
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { Expect, Equals } from './expect-type'
2+
3+
type _ = Expect<
4+
Equals<
5+
typeof import('next/root-params'),
6+
{
7+
id: () => Promise<string | undefined>
8+
// new types will be patched in here when a test adds new root params
9+
}
10+
>
11+
>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type Expect<T extends true> = T
2+
export type Equals<A, B> =
3+
(<G>() => G extends A ? 1 : 2) extends <G>() => G extends B ? 1 : 2
4+
? true
5+
: false
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Expect, Equals } from './expect-type'
2+
3+
type _ = Expect<
4+
Equals<
5+
typeof import('next/root-params'),
6+
{
7+
lang: () => Promise<string | undefined>
8+
locale: () => Promise<string | undefined>
9+
path: () => Promise<string[] | undefined>
10+
}
11+
>
12+
>

test/e2e/app-dir/app-root-params-getters/multiple-roots.test.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import { nextTestSetup } from 'e2e-utils'
22
import { join } from 'path'
33
import { createSandbox } from 'development-sandbox'
44
import { outdent } from 'outdent'
5-
import { retry } from '../../../lib/next-test-utils'
5+
import { retry } from 'next-test-utils'
6+
import { expectTypecheckingSuccess } from './typecheck'
67

78
describe('app-root-param-getters - multiple roots', () => {
89
const { next, isNextDev, isTurbopack } = nextTestSetup({
910
files: join(__dirname, 'fixtures', 'multiple-roots'),
11+
dependencies: {
12+
typescript: '5.8.3',
13+
},
1014
})
1115

1216
it('should have root params on dashboard pages', async () => {
@@ -32,6 +36,10 @@ describe('app-root-param-getters - multiple roots', () => {
3236
`hello world ${JSON.stringify({ id: '1' })}`
3337
)
3438

39+
await expectTypecheckingSuccess(next)
40+
41+
const originalTypeTestsSource = await next.readFile('type-tests.ts')
42+
3543
// Add a new root layout with a root param.
3644
// This should make the bundler re-generate 'next/root-params' with a new getter for `stuff`.
3745
const newRootLayoutFiles = new Map([
@@ -64,6 +72,13 @@ describe('app-root-param-getters - multiple roots', () => {
6472
}
6573
`,
6674
],
75+
[
76+
'type-tests.ts',
77+
originalTypeTestsSource.replace(
78+
'// new types will be patched in here when a test adds new root params',
79+
'stuff: () => Promise<string | undefined>'
80+
),
81+
],
6782
])
6883
for (const [filePath, fileContent] of newRootLayoutFiles) {
6984
await session.write(filePath, fileContent)
@@ -78,8 +93,11 @@ describe('app-root-param-getters - multiple roots', () => {
7893
)
7994
})
8095

96+
await expectTypecheckingSuccess(next)
97+
8198
// Change the name of the root param
8299
// This should make the bundler re-generate 'next/root-params' again, with `things` instead of `stuff`.
100+
83101
if (isTurbopack) {
84102
// FIXME(turbopack): Something in our routing logic doesn't handle renaming a route param in turbopack mode.
85103
// I haven't found the cause for this, but `DefaultRouteMatcherManager.reload` calls
@@ -89,6 +107,7 @@ describe('app-root-param-getters - multiple roots', () => {
89107
// so we're skipping the rest of the test for now.
90108
return
91109
}
110+
92111
await session.renameFolder(
93112
'app/new-root/[stuff]',
94113
'app/new-root/[things]'
@@ -104,9 +123,10 @@ describe('app-root-param-getters - multiple roots', () => {
104123
})
105124

106125
// Update the page to use the new root param name
107-
await session.write(
108-
'app/new-root/[things]/page.tsx',
109-
outdent`
126+
const updatedRootLayoutFiles = new Map([
127+
[
128+
'app/new-root/[things]/page.tsx',
129+
outdent`
110130
import { id, things } from 'next/root-params'
111131
export default async function Page() {
112132
return (
@@ -117,8 +137,20 @@ describe('app-root-param-getters - multiple roots', () => {
117137
export async function generateStaticParams() {
118138
return [{ things: '123' }]
119139
}
120-
`
121-
)
140+
`,
141+
],
142+
[
143+
// Type declarations should have been regenerated
144+
'type-tests.ts',
145+
originalTypeTestsSource.replace(
146+
'// new types will be patched in here when a test adds new root params',
147+
'things: () => Promise<string | undefined>'
148+
),
149+
],
150+
])
151+
for (const [filePath, fileContent] of updatedRootLayoutFiles) {
152+
await session.write(filePath, fileContent)
153+
}
122154

123155
// The page should call the getter and get the correct param value.
124156
await retry(async () => {
@@ -128,6 +160,14 @@ describe('app-root-param-getters - multiple roots', () => {
128160
`hello new root: ${JSON.stringify(params)}`
129161
)
130162
})
163+
164+
if (!isTurbopack) {
165+
// FIXME: seems like `next-types-plugin` doesn't clean up declarations for removed routes,
166+
// which causes a typechecking error (because it references layout files that we renamed)
167+
await next.remove('.next/types/app/new-root/[stuff]')
168+
}
169+
170+
await expectTypecheckingSuccess(next)
131171
})
132172
}
133173
})

test/e2e/app-dir/app-root-params-getters/simple.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,25 @@ import { join } from 'path'
44
import { createSandbox } from 'development-sandbox'
55
import { outdent } from 'outdent'
66
import { createRequestTracker } from '../../../lib/e2e-utils/request-tracker'
7+
import { expectTypecheckingSuccess } from './typecheck'
78

89
describe('app-root-param-getters - simple', () => {
910
const { next, isNextDev, isTurbopack, isNextDeploy } = nextTestSetup({
1011
files: join(__dirname, 'fixtures', 'simple'),
12+
dependencies: {
13+
typescript: '5.8.3',
14+
},
1115
})
1216

1317
it('should allow reading root params', async () => {
1418
const params = { lang: 'en', locale: 'us' }
1519
const $ = await next.render$(`/${params.lang}/${params.locale}`)
1620
expect($('p').text()).toBe(`hello world ${JSON.stringify(params)}`)
21+
22+
if (isNextDev) {
23+
// in dev, we need to typecheck manually. note that we have to compile at least one page to get the types
24+
await expectTypecheckingSuccess(next)
25+
}
1726
})
1827

1928
it('should allow reading root params in nested pages', async () => {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import execa from 'execa'
2+
import type { NextInstance } from '../../../lib/e2e-utils'
3+
4+
export async function expectTypecheckingSuccess(next: NextInstance) {
5+
const result = await execa('pnpm', ['tsc', '--noEmit'], {
6+
reject: false,
7+
cwd: next.testDir,
8+
})
9+
if (result.exitCode !== 0) {
10+
throw new Error(
11+
`Typechecking failed with exit code ${result.exitCode}:\n` + result.all
12+
)
13+
}
14+
}

0 commit comments

Comments
 (0)