Skip to content

Commit b71a222

Browse files
authored
test: Add multitenancy test for Next.js (#917)
1 parent 48cae6b commit b71a222

File tree

63 files changed

+1262
-71
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1262
-71
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createTest, type TestConfig } from 'e2e-shared/create-test'
2+
import { getOptionsUrl } from 'e2e-shared/lib/options'
3+
4+
function testMultiTenant(
5+
options: TestConfig & {
6+
expectedPathname: string
7+
}
8+
) {
9+
const factory = createTest('Multitenant', ({ path }) => {
10+
for (const shallow of [true, false]) {
11+
for (const history of ['replace', 'push'] as const) {
12+
it(`Updates with ({ shallow: ${shallow}, history: ${history} })`, () => {
13+
cy.visit(getOptionsUrl(path, { shallow, history }))
14+
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
15+
cy.get('#client-state').should('be.empty')
16+
cy.get('#server-state').should('be.empty')
17+
cy.get('#client-tenant').should('have.text', 'david')
18+
cy.get('#server-tenant').should('have.text', 'david')
19+
cy.get('#router-pathname').should(
20+
'have.text',
21+
options.expectedPathname
22+
)
23+
cy.get('button').click()
24+
cy.get('#client-state').should('have.text', 'pass')
25+
cy.get('#client-tenant').should('have.text', 'david')
26+
cy.get('#server-tenant').should('have.text', 'david')
27+
cy.get('#router-pathname').should(
28+
'have.text',
29+
options.expectedPathname
30+
)
31+
if (shallow === false) {
32+
cy.get('#server-state').should('have.text', 'pass')
33+
} else {
34+
cy.get('#server-state').should('be.empty')
35+
}
36+
if (history !== 'push') {
37+
return
38+
}
39+
cy.go('back')
40+
cy.get('#client-tenant').should('have.text', 'david')
41+
cy.get('#server-tenant').should('have.text', 'david')
42+
cy.get('#client-state').should('be.empty')
43+
cy.get('#server-state').should('be.empty')
44+
cy.get('#router-pathname').should(
45+
'have.text',
46+
options.expectedPathname
47+
)
48+
})
49+
}
50+
}
51+
})
52+
53+
return factory(options)
54+
}
55+
56+
testMultiTenant({
57+
path: '/app/multitenant',
58+
nextJsRouter: 'app',
59+
description: 'Dynamic route',
60+
expectedPathname: '/app/multitenant'
61+
})
62+
63+
testMultiTenant({
64+
path: '/pages/multitenant',
65+
nextJsRouter: 'pages',
66+
description: 'Dynamic route',
67+
expectedPathname: '/pages/multitenant/[tenant]'
68+
})
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { testDynamicSegments } from 'e2e-shared/specs/dynamic-segments.cy'
2+
3+
testDynamicSegments({
4+
path: '/app/dynamic-segments/dynamic/segment',
5+
expectedSegments: ['segment'],
6+
nextJsRouter: 'app'
7+
})
8+
9+
testDynamicSegments({
10+
path: '/pages/dynamic-segments/dynamic/segment',
11+
expectedSegments: ['segment'],
12+
nextJsRouter: 'pages'
13+
})
14+
15+
// Catch-all --
16+
17+
testDynamicSegments({
18+
path: '/app/dynamic-segments/catch-all/foo',
19+
expectedSegments: ['foo'],
20+
nextJsRouter: 'app'
21+
})
22+
23+
testDynamicSegments({
24+
path: '/pages/dynamic-segments/catch-all/foo',
25+
expectedSegments: ['foo'],
26+
nextJsRouter: 'pages'
27+
})
28+
29+
testDynamicSegments({
30+
path: '/app/dynamic-segments/catch-all/a/b/c',
31+
expectedSegments: ['a', 'b', 'c'],
32+
nextJsRouter: 'app'
33+
})
34+
35+
testDynamicSegments({
36+
path: '/pages/dynamic-segments/catch-all/a/b/c',
37+
expectedSegments: ['a', 'b', 'c'],
38+
nextJsRouter: 'pages'
39+
})
40+
41+
// Optional catch-all --
42+
43+
testDynamicSegments({
44+
path: '/app/dynamic-segments/optional-catch-all', // no segments
45+
expectedSegments: [],
46+
nextJsRouter: 'app'
47+
})
48+
49+
testDynamicSegments({
50+
path: '/pages/dynamic-segments/optional-catch-all', // no segments
51+
expectedSegments: [],
52+
nextJsRouter: 'pages'
53+
})
54+
55+
testDynamicSegments({
56+
path: '/app/dynamic-segments/optional-catch-all/foo',
57+
expectedSegments: ['foo'],
58+
nextJsRouter: 'app'
59+
})
60+
61+
testDynamicSegments({
62+
path: '/pages/dynamic-segments/optional-catch-all/foo',
63+
expectedSegments: ['foo'],
64+
nextJsRouter: 'pages'
65+
})
66+
67+
testDynamicSegments({
68+
path: '/app/dynamic-segments/optional-catch-all/a/b/c',
69+
expectedSegments: ['a', 'b', 'c'],
70+
nextJsRouter: 'app'
71+
})
72+
73+
testDynamicSegments({
74+
path: '/pages/dynamic-segments/optional-catch-all/a/b/c',
75+
expectedSegments: ['a', 'b', 'c'],
76+
nextJsRouter: 'pages'
77+
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { testPrettyUrls } from 'e2e-shared/specs/pretty-urls.cy'
2+
3+
testPrettyUrls({
4+
path: '/app/pretty-urls',
5+
nextJsRouter: 'app'
6+
})
7+
8+
testPrettyUrls({
9+
path: '/pages/pretty-urls',
10+
nextJsRouter: 'pages'
11+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client'
2+
3+
import { DisplaySegments } from 'e2e-shared/specs/dynamic-segments'
4+
import { useParams } from 'next/navigation'
5+
import { ReactNode } from 'react'
6+
7+
export function ClientSegment({ children }: { children?: ReactNode }) {
8+
const params = useParams()
9+
const segments = params?.segments as string[]
10+
return (
11+
<>
12+
{children}
13+
<DisplaySegments environment="client" segments={segments} />
14+
</>
15+
)
16+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Display } from 'e2e-shared/components/display'
2+
import { DisplaySegments, UrlControls } from 'e2e-shared/specs/dynamic-segments'
3+
import { createLoader, parseAsString, type SearchParams } from 'nuqs/server'
4+
import { Suspense } from 'react'
5+
import { ClientSegment } from './client'
6+
7+
type PageProps = {
8+
params: Promise<{ segments: string[] }>
9+
searchParams: Promise<SearchParams>
10+
}
11+
12+
const loadTest = createLoader({
13+
test: parseAsString
14+
})
15+
16+
export default async function DynamicPage(props: PageProps) {
17+
const searchParams = await props.searchParams
18+
const { segments } = await props.params
19+
const { test: serverState } = loadTest(searchParams)
20+
return (
21+
<>
22+
<Suspense>
23+
<UrlControls>
24+
<Display environment="server" state={serverState} />
25+
</UrlControls>
26+
</Suspense>
27+
<Suspense>
28+
<ClientSegment>
29+
<DisplaySegments environment="server" segments={segments} />
30+
</ClientSegment>
31+
</Suspense>
32+
</>
33+
)
34+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client'
2+
3+
import { DisplaySegments } from 'e2e-shared/specs/dynamic-segments'
4+
import { useParams } from 'next/navigation'
5+
import { ReactNode } from 'react'
6+
7+
export function ClientSegment({ children }: { children?: ReactNode }) {
8+
const params = useParams()
9+
const segment = params?.segment as string
10+
return (
11+
<>
12+
{children}
13+
<DisplaySegments environment="client" segments={[segment]} />
14+
</>
15+
)
16+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Display } from 'e2e-shared/components/display'
2+
import { DisplaySegments, UrlControls } from 'e2e-shared/specs/dynamic-segments'
3+
import { createLoader, parseAsString, type SearchParams } from 'nuqs/server'
4+
import { Suspense } from 'react'
5+
import { ClientSegment } from './client'
6+
7+
type PageProps = {
8+
params: Promise<{ segment: string }>
9+
searchParams: Promise<SearchParams>
10+
}
11+
12+
const loadTest = createLoader({
13+
test: parseAsString
14+
})
15+
16+
export default async function DynamicPage(props: PageProps) {
17+
const searchParams = await props.searchParams
18+
const { segment } = await props.params
19+
const { test: serverState } = loadTest(searchParams)
20+
return (
21+
<>
22+
<Suspense>
23+
<UrlControls>
24+
<Display environment="server" state={serverState} />
25+
</UrlControls>
26+
</Suspense>
27+
<Suspense>
28+
<ClientSegment>
29+
<DisplaySegments environment="server" segments={[segment]} />
30+
</ClientSegment>
31+
</Suspense>
32+
</>
33+
)
34+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use client'
2+
3+
import { DisplaySegments } from 'e2e-shared/specs/dynamic-segments'
4+
import { useParams } from 'next/navigation'
5+
import { ReactNode } from 'react'
6+
7+
export function ClientSegment({ children }: { children?: ReactNode }) {
8+
const params = useParams()
9+
const segments = params?.segments as string[]
10+
return (
11+
<>
12+
{children}
13+
<DisplaySegments environment="client" segments={segments} />
14+
</>
15+
)
16+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Display } from 'e2e-shared/components/display'
2+
import { DisplaySegments, UrlControls } from 'e2e-shared/specs/dynamic-segments'
3+
import { createLoader, parseAsString, type SearchParams } from 'nuqs/server'
4+
import { Suspense } from 'react'
5+
import { ClientSegment } from './client'
6+
7+
type PageProps = {
8+
params: Promise<{ segments: string[] }>
9+
searchParams: Promise<SearchParams>
10+
}
11+
12+
const loadTest = createLoader({
13+
test: parseAsString
14+
})
15+
16+
export default async function DynamicPage(props: PageProps) {
17+
const searchParams = await props.searchParams
18+
const { segments } = await props.params
19+
const { test: serverState } = loadTest(searchParams)
20+
return (
21+
<>
22+
<Suspense>
23+
<UrlControls>
24+
<Display environment="server" state={serverState} />
25+
</UrlControls>
26+
</Suspense>
27+
<Suspense>
28+
<ClientSegment>
29+
<DisplaySegments environment="server" segments={segments} />
30+
</ClientSegment>
31+
</Suspense>
32+
</>
33+
)
34+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { PrettyUrls } from 'e2e-shared/specs/pretty-urls'
2+
import { Suspense } from 'react'
3+
4+
export default function Page() {
5+
return (
6+
<Suspense>
7+
<PrettyUrls />
8+
</Suspense>
9+
)
10+
}

0 commit comments

Comments
 (0)