Skip to content

Commit

Permalink
test: Add multitenancy test for Next.js
Browse files Browse the repository at this point in the history
Should fail to preserve the router pathname in the pages router with shallow: true.
  • Loading branch information
franky47 committed Feb 14, 2025
1 parent c23f838 commit 419b4a6
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 0 deletions.
66 changes: 66 additions & 0 deletions packages/e2e/next/cypress/e2e/multitenant.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { createTest, type TestConfig } from 'e2e-shared/create-test'
import { getShallowUrl } from 'e2e-shared/specs/shallow.defs'

function testMultiTenant(options: TestConfig) {
const factory = createTest('Multitenant', ({ path }) => {
for (const shallow of [true, false]) {
for (const history of ['replace', 'push'] as const) {
it(`Updates with ({ shallow: ${shallow}, history: ${history} })`, () => {
cy.visit(getShallowUrl(path, { shallow, history }))
cy.contains('#hydration-marker', 'hydrated').should('be.hidden')
cy.get('#client-state').should('be.empty')
cy.get('#server-state').should('be.empty')
cy.get('#client-tenant').should('have.text', 'david')
cy.get('#server-tenant').should('have.text', 'david')
cy.get('#router-pathname').should(
'have.text',
options.nextJsRouter === 'pages'
? '/pages/multitenant/[tenant]'
: '/app/multitenant'
)
cy.get('button').click()
cy.get('#client-state').should('have.text', 'pass')
cy.get('#client-tenant').should('have.text', 'david')
cy.get('#server-tenant').should('have.text', 'david')
cy.get('#router-pathname').should(
'have.text',
options.nextJsRouter === 'pages'
? '/pages/multitenant/[tenant]'
: '/app/multitenant'
)
if (shallow === false) {
cy.get('#server-state').should('have.text', 'pass')
} else {
cy.get('#server-state').should('be.empty')
}
if (history !== 'push') {
return
}
cy.go('back')
cy.get('#client-tenant').should('have.text', 'david')
cy.get('#server-tenant').should('have.text', 'david')
cy.get('#client-state').should('be.empty')
cy.get('#server-state').should('be.empty')
cy.get('#router-pathname').should(
'have.text',
options.nextJsRouter === 'pages'
? '/pages/multitenant/[tenant]'
: '/app/multitenant'
)
})
}
}
})

return factory(options)
}

testMultiTenant({
path: '/app/multitenant',
nextJsRouter: 'app'
})

testMultiTenant({
path: '/pages/multitenant',
nextJsRouter: 'pages'
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client'

import { useParams, usePathname } from 'next/navigation'

export function TenantClient() {
const params = useParams()
const pathname = usePathname()
return (
<>
<p id="client-tenant">{params?.tenant}</p>
<p id="router-pathname">{pathname}</p>
</>
)
}
46 changes: 46 additions & 0 deletions packages/e2e/next/src/app/app/multitenant/[tenant]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { ShallowUseQueryState } from 'e2e-shared/specs/shallow'
import { ShallowDisplay } from 'e2e-shared/specs/shallow-display'
import {
createSearchParamsCache,
parseAsString,
type SearchParams
} from 'nuqs/server'
import { Suspense } from 'react'
import { TenantClient } from './client-tenant'

type PageProps = {
params: Promise<{ tenant: string }>
searchParams: Promise<SearchParams>
}

const cache = createSearchParamsCache(
{
state: parseAsString
},
{
urlKeys: {
state: 'test'
}
}
)

export default async function TenantPage({ params, searchParams }: PageProps) {
const { tenant } = await params
if (!tenant) {
return <div>Error: Tenant not found.</div>
}
await cache.parse(searchParams)

return (
<>
<Suspense>
<ShallowUseQueryState />
</Suspense>
<ShallowDisplay environment="server" state={cache.get('state')} />
<p id="server-tenant">{tenant}</p>
<Suspense>
<TenantClient />
</Suspense>
</>
)
}
22 changes: 22 additions & 0 deletions packages/e2e/next/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NextRequest, NextResponse } from 'next/server'

export const config = {
matcher: ['/app/multitenant', '/pages/multitenant']
}

export default async function middleware(req: NextRequest) {
// https://media1.tenor.com/m/YrcMb6KRczsAAAAC/doctor-who-dr-who.gif
const tenant = 'david'
const pathname = req.nextUrl.pathname
if (pathname === '/app/multitenant') {
const url = new URL(`/app/multitenant/${tenant}`, req.url)
url.search = req.nextUrl.search
return NextResponse.rewrite(url)
}
if (pathname === '/pages/multitenant') {
const url = new URL(`/pages/multitenant/${tenant}`, req.url)
url.search = req.nextUrl.search
return NextResponse.rewrite(url)
}
return NextResponse.next()
}
8 changes: 8 additions & 0 deletions packages/e2e/next/src/pages/pages/middleware.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function MiddlewarePage() {
return (
<div>
<h1>Client</h1>
<p>Pages router</p>
</div>
)
}
45 changes: 45 additions & 0 deletions packages/e2e/next/src/pages/pages/multitenant/[tenant].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ShallowUseQueryState } from 'e2e-shared/specs/shallow'
import { ShallowDisplay } from 'e2e-shared/specs/shallow-display'
import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next'
import { useParams } from 'next/navigation'
import { useRouter } from 'next/router'

type Props = {
serverState: string | null
tenant: string | null
}

export default function Page({ serverState, tenant }: Props) {
const params = useParams()
const router = useRouter()
return (
<>
<ShallowUseQueryState />
<ShallowDisplay environment="server" state={serverState} />
<p id="server-tenant">{tenant}</p>
<p id="client-tenant">{params?.tenant}</p>
<p id="router-pathname">{router.pathname}</p>
</>
)
}

export function getServerSideProps(
ctx: GetServerSidePropsContext
): GetServerSidePropsResult<Props> {
const tenant = ctx.params?.tenant as string | null

if (!tenant) {
return {
notFound: true
}
}

const serverState = (ctx.query.test as string) ?? null

return {
props: {
serverState,
tenant
}
}
}

0 comments on commit 419b4a6

Please sign in to comment.