Skip to content

Commit afe3ab5

Browse files
test(nextjs-mf): extract app-router fixtures
1 parent 3b86cb5 commit afe3ab5

28 files changed

Lines changed: 871 additions & 273 deletions

File tree

.changeset/tall-buses-explode.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@module-federation/nextjs-mf': patch
3+
'@module-federation/node': patch
4+
'@module-federation/enhanced': patch
5+
'@module-federation/manifest': patch
6+
'@module-federation/modern-js': patch
7+
'@module-federation/modern-js-v3': patch
8+
'@module-federation/sdk': patch
9+
---
10+
11+
Stabilize the Next.js v9 integration path with improved Next.js 16/RSC behavior, hydration handling, and E2E coverage.
12+
13+
This also aligns supporting runtime/build plumbing across Node runtime helpers, enhanced runtime/container hooks, manifest generation, Modern.js integrations, and SDK webpack-path normalization used by these flows.

apps/next-app-router/next-app-router-4000/app/context/context-click-counter.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
'use client';
22

33
import { useCounter } from './counter-context';
4-
import React from 'react';
4+
import React, { Suspense } from 'react';
55
import { Boundary } from '#/ui/boundary';
6-
import dynamic from 'next/dynamic';
7-
const Button = dynamic(() => import('remote_4001/Button'), { ssr: true });
6+
7+
const Button = React.lazy(() => import('remote_4001/Button'));
88

99
const ContextClickCounter = () => {
1010
const [count, setCount] = useCounter();
@@ -16,7 +16,15 @@ const ContextClickCounter = () => {
1616
size="small"
1717
animateRerendering={false}
1818
>
19-
<Button>testing</Button>
19+
<Suspense
20+
fallback={
21+
<button className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 hover:bg-gray-500 hover:text-white">
22+
testing
23+
</button>
24+
}
25+
>
26+
<Button>testing</Button>
27+
</Suspense>
2028
<button
2129
onClick={() => setCount(count + 1)}
2230
className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium tabular-nums text-gray-100 hover:bg-gray-500 hover:text-white"

apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/[subCategorySlug]/error.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client';
22

33
import { Boundary } from '#/ui/boundary';
4-
import Button from 'remote_4001/Button';
5-
import React from 'react';
4+
import React, { Suspense } from 'react';
5+
6+
const Button = React.lazy(() => import('remote_4001/Button'));
67

78
export default function Error({ error, reset }: any) {
89
React.useEffect(() => {
@@ -18,7 +19,15 @@ export default function Error({ error, reset }: any) {
1819
<h2 className="text-lg font-bold">Error</h2>
1920
<p className="text-sm">{error?.message}</p>
2021
<div>
21-
<Button onClick={() => reset()}>Try Again</Button>
22+
<Suspense
23+
fallback={
24+
<button className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 hover:bg-gray-500 hover:text-white">
25+
Try Again
26+
</button>
27+
}
28+
>
29+
<Button onClick={() => reset()}>Try Again</Button>
30+
</Suspense>
2231
</div>
2332
</div>
2433
</Boundary>

apps/next-app-router/next-app-router-4000/app/error-handling/[categorySlug]/error.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client';
22

33
import { Boundary } from '#/ui/boundary';
4-
import Button from 'remote_4001/Button';
5-
import React from 'react';
4+
import React, { Suspense } from 'react';
5+
6+
const Button = React.lazy(() => import('remote_4001/Button'));
67

78
export default function Error({ error, reset }: any) {
89
React.useEffect(() => {
@@ -15,7 +16,15 @@ export default function Error({ error, reset }: any) {
1516
<h2 className="text-lg font-bold">Error</h2>
1617
<p className="text-sm">{error?.message}</p>
1718
<div>
18-
<Button onClick={() => reset()}>Try Again</Button>
19+
<Suspense
20+
fallback={
21+
<button className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 hover:bg-gray-500 hover:text-white">
22+
Try Again
23+
</button>
24+
}
25+
>
26+
<Button onClick={() => reset()}>Try Again</Button>
27+
</Suspense>
1928
</div>
2029
</div>
2130
</Boundary>

apps/next-app-router/next-app-router-4000/app/error-handling/error.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client';
22

33
import { Boundary } from '#/ui/boundary';
4-
import Button from 'remote_4001/Button';
5-
import React from 'react';
4+
import React, { Suspense } from 'react';
5+
6+
const Button = React.lazy(() => import('remote_4001/Button'));
67

78
export default function Error({ error, reset }: any) {
89
React.useEffect(() => {
@@ -15,7 +16,15 @@ export default function Error({ error, reset }: any) {
1516
<h2 className="text-lg font-bold">Error</h2>
1617
<p className="text-sm">{error?.message}</p>
1718
<div>
18-
<Button onClick={() => reset()}>Try Again</Button>
19+
<Suspense
20+
fallback={
21+
<button className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 hover:bg-gray-500 hover:text-white">
22+
Try Again
23+
</button>
24+
}
25+
>
26+
<Button onClick={() => reset()}>Try Again</Button>
27+
</Suspense>
1928
</div>
2029
</div>
2130
</Boundary>

apps/next-app-router/next-app-router-4000/app/page.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { demos } from '#/lib/demos';
22
import Link from 'next/link';
3-
import dynamic from 'next/dynamic';
4-
const Button = dynamic(() => import('remote_4001/Button'), { ssr: true });
3+
import RemoteButton from './remote-button';
4+
5+
export const dynamic = 'force-dynamic';
56

67
export default function Page() {
78
return (
89
<div className="space- y-8">
9-
<Button>Button from remote</Button>
10+
<RemoteButton>Button from remote</RemoteButton>
1011
<h1 className="text-xl font-medium text-gray-300">Examples</h1>
1112

1213
<div className="space-y-10 text-white">
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client';
2+
3+
import React, { Suspense } from 'react';
4+
5+
const Button = React.lazy(() => import('remote_4001/Button'));
6+
7+
export default function RemoteButton({
8+
children,
9+
}: {
10+
children: React.ReactNode;
11+
}) {
12+
return (
13+
<Suspense
14+
fallback={
15+
<button className="rounded-lg bg-gray-700 px-3 py-1 text-sm font-medium text-gray-100 hover:bg-gray-500 hover:text-white">
16+
{children}
17+
</button>
18+
}
19+
>
20+
<Button>{children}</Button>
21+
</Suspense>
22+
);
23+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import StyledJsxRegistry from './registry';
22

3+
export const dynamic = 'force-dynamic';
4+
35
export default function Layout({ children }: { children: React.ReactNode }) {
46
return <StyledJsxRegistry>{children}</StyledJsxRegistry>;
57
}

apps/next-app-router/next-app-router-4000/app/styling/styled-jsx/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client';
22

3+
export const dynamic = 'force-dynamic';
4+
35
const SkeletonCard = () => (
46
<>
57
<div className="skeleton">
Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,28 @@
1-
import { getH1 } from '../support/app.po';
2-
3-
describe('next-app-router-4000', () => {
1+
describe('next-app-router-4000 federation host', () => {
42
beforeEach(() => cy.visit('/'));
53

6-
describe('Home page', () => {
7-
it('should display examples heading', () => {
8-
getH1().contains('Examples');
9-
});
10-
11-
it('should have remote button from module federation', () => {
12-
cy.get('button').contains('Button from remote').should('exist');
13-
});
14-
15-
it('should have navigation links', () => {
16-
cy.get('a[href="/layouts"]').should('exist');
17-
cy.get('a[href="/error-handling"]').should('exist');
18-
cy.get('a[href="/loading"]').should('exist');
19-
});
4+
it('renders app shell and remote button on the home route', () => {
5+
cy.contains('h1', 'Examples').should('be.visible');
6+
cy.contains('button', 'Button from remote').should('be.visible');
7+
cy.get('a[href="/layouts"]').should('exist');
8+
cy.get('a[href="/parallel-routes"]').should('exist');
209
});
2110

22-
describe('Navigation', () => {
23-
it('should navigate to layouts page', () => {
24-
cy.get('a[href="/layouts"]').first().click();
25-
cy.url().should('include', '/layouts');
26-
});
27-
28-
it('should navigate to parallel routes', () => {
29-
cy.get('a[href="/parallel-routes"]').first().click();
30-
cy.url().should('include', '/parallel-routes');
31-
});
11+
it('keeps federated remote components working after app-router navigation', () => {
12+
cy.contains('a', 'Client Context').click();
13+
cy.url().should('include', '/context');
14+
cy.contains('button', 'testing').should('be.visible');
15+
cy.contains('button', '0 Clicks').click();
16+
cy.contains('button', '1 Clicks').should('be.visible');
3217
});
3318

34-
describe('Module Federation', () => {
35-
it('should load remote button component', () => {
36-
cy.get('button').contains('Button from remote').should('be.visible');
37-
});
19+
it('can fetch the 4001 remote container from the host test run', () => {
20+
cy.request('http://localhost:4001/_next/static/chunks/remoteEntry.js').then(
21+
({ status, body }) => {
22+
expect(status).to.eq(200);
23+
expect(body).to.include('./Button');
24+
expect(body).to.include('./GlobalNav');
25+
},
26+
);
3827
});
3928
});

0 commit comments

Comments
 (0)