Skip to content

Commit f47722e

Browse files
ScriptedAlchemywtlin1228claude
authored
test: trigger CI workflow (#3890)
Co-authored-by: Wei Tang Lin <wtlin1228@gmail.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent e15d158 commit f47722e

File tree

34 files changed

+815
-365
lines changed

34 files changed

+815
-365
lines changed

.github/workflows/build-and-test.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,8 @@ jobs:
130130
needs: checkout-install
131131
uses: ./.github/workflows/e2e-router.yml
132132
secrets: inherit
133+
134+
e2e-next-app-router:
135+
needs: checkout-install
136+
uses: ./.github/workflows/e2e-next-app-router.yml
137+
secrets: inherit
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: E2E Test for Next.js App Router
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
e2e-next-app-router:
8+
runs-on: ubuntu-latest
9+
timeout-minutes: 30
10+
steps:
11+
- name: Checkout Repository
12+
uses: actions/checkout@v3
13+
with:
14+
fetch-depth: 0
15+
16+
- name: Install Pnpm
17+
run: |
18+
corepack prepare pnpm@8.11.0 --activate
19+
corepack enable
20+
21+
- name: Setup Node.js 18
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: '18'
25+
cache: 'pnpm'
26+
27+
- name: Set Nx SHA
28+
uses: nrwl/nx-set-shas@v3
29+
30+
- name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable
31+
run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> $GITHUB_ENV
32+
33+
- name: Set local webpack
34+
run: echo "NEXT_PRIVATE_LOCAL_WEBPACK=true" >> $GITHUB_ENV
35+
36+
- name: Install Dependencies
37+
run: pnpm install
38+
39+
- name: Install Cypress
40+
run: npx cypress install
41+
42+
- name: Run Build for All
43+
run: npx nx run-many --targets=build --projects=tag:type:pkg
44+
45+
- name: Run condition check script
46+
id: check-ci
47+
run: node tools/scripts/ci-is-affected.mjs --appName=next-app-router-4000,next-app-router-4001
48+
49+
- name: E2E Test for Next.js App Router
50+
if: steps.check-ci.outcome == 'success'
51+
run: npx kill-port --port 4000,4001 || true && pnpm run app:next-router:dev & echo "done" && sleep 25 && npx nx run-many --target=e2e --projects=next-app-router-4000,next-app-router-4001 --parallel=1 && lsof -ti tcp:4000,4001 | xargs kill || true

apps/next-app-router/next-app-router-4000/cypress.config.ts

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,11 @@
11
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
22
import { defineConfig } from 'cypress';
3-
import { execSync } from 'child_process';
43

54
export default defineConfig({
6-
projectId: 'next-app-router-4000',
5+
projectId: '27e40c91-5ac3-4433-8a87-651d10f51cf6',
76
e2e: {
87
...nxE2EPreset(__filename, { cypressDir: 'cypress' }),
9-
// Please ensure you use `cy.origin()` when navigating between domains and remove this option.
10-
// See https://docs.cypress.io/app/references/migration-guide#Changes-to-cyorigin
11-
injectDocumentDomain: true,
12-
setupNodeEvents(on, config) {
13-
// Kill servers when ALL tests are done (not after each test file)
14-
on('after:run', (results) => {
15-
console.log('🧹 All tests completed, cleaning up servers...');
16-
console.log(
17-
`📊 Test results: ${results.totalPassed} passed, ${results.totalFailed} failed`,
18-
);
19-
20-
try {
21-
console.log('🔪 Killing servers on ports 4000 and 4001...');
22-
execSync('npx kill-port 4000 4001', { stdio: 'inherit' });
23-
console.log('✅ Server cleanup completed successfully');
24-
} catch (error) {
25-
console.log(
26-
'⚠️ Error during server cleanup (servers may already be closed):',
27-
error.message,
28-
);
29-
}
30-
});
31-
32-
// Log when each spec starts (but don't kill servers)
33-
on('before:spec', (spec) => {
34-
console.log(`🚀 Starting spec: ${spec.name}`);
35-
});
36-
37-
// Log when each spec ends (but don't kill servers)
38-
on('after:spec', (spec, results) => {
39-
console.log(
40-
`✨ Completed spec: ${spec.name} - ${results.stats.passes} passed, ${results.stats.failures} failed`,
41-
);
42-
});
43-
},
8+
baseUrl: 'http://localhost:4000',
449
},
4510
defaultCommandTimeout: 20000,
4611
retries: {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { getH1 } from '../support/app.po';
2+
3+
describe('next-app-router-4000', () => {
4+
beforeEach(() => cy.visit('/'));
5+
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+
});
20+
});
21+
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+
});
32+
});
33+
34+
describe('Module Federation', () => {
35+
it('should load remote button component', () => {
36+
cy.get('button').contains('Button from remote').should('be.visible');
37+
});
38+
});
39+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "hello@cypress.io",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const getH1 = () => cy.get('h1');
2+
export const getH2 = () => cy.get('h2');
3+
export const getH3 = () => cy.get('h3');
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"extends": "../tsconfig.json",
3+
"compilerOptions": {
4+
"sourceMap": false,
5+
"outDir": "../../../dist/out-tsc",
6+
"allowJs": true,
7+
"types": ["cypress", "node"],
8+
"esModuleInterop": true,
9+
"forceConsistentCasingInFileNames": true,
10+
"strict": true,
11+
"skipLibCheck": true
12+
},
13+
"include": ["**/*.ts", "**/*.js", "../cypress.config.ts"]
14+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//@ts-check
2+
'use client';
3+
4+
import dynamic from 'next/dynamic';
5+
6+
// Dynamically import remote components
7+
const Button = dynamic(() => import('remote_4001/Button'), { ssr: true });
8+
const Header = dynamic(() => import('remote_4001/Header'), { ssr: true });
9+
const ProductCard = dynamic(() => import('remote_4001/ProductCard'), {
10+
ssr: true,
11+
});
12+
const TabGroup = dynamic(() => import('remote_4001/TabGroup'), { ssr: true });
13+
const TabNavItem = dynamic(() => import('remote_4001/TabNavItem'), {
14+
ssr: true,
15+
});
16+
const CountUp = dynamic(() => import('remote_4001/CountUp'), { ssr: true });
17+
const RenderingInfo = dynamic(() => import('remote_4001/RenderingInfo'), {
18+
ssr: true,
19+
});
20+
21+
export default function DemoPage() {
22+
return (
23+
<div className="p-4">
24+
<Header />
25+
26+
<main className="mx-auto mt-8 max-w-4xl">
27+
<h1 className="mb-6 text-2xl font-bold">Remote Components Demo</h1>
28+
29+
<section className="mb-8">
30+
<h2 className="mb-4 text-xl font-semibold">Basic UI Components</h2>
31+
<div className="space-x-4">
32+
<Button>Primary Button</Button>
33+
<Button>Secondary Button</Button>
34+
</div>
35+
</section>
36+
37+
<section className="mb-8">
38+
<h2 className="mb-4 text-xl font-semibold">Navigation Components</h2>
39+
<TabGroup>
40+
<TabNavItem href="/demo/tab1" isActive={true}>
41+
Tab 1
42+
</TabNavItem>
43+
<TabNavItem href="/demo/tab2" isActive={false}>
44+
Tab 2
45+
</TabNavItem>
46+
<TabNavItem href="/demo/tab3" isActive={false}>
47+
Tab 3
48+
</TabNavItem>
49+
</TabGroup>
50+
</section>
51+
52+
<section className="mb-8">
53+
<h2 className="mb-4 text-xl font-semibold">Product Components</h2>
54+
<div className="grid grid-cols-2 gap-4">
55+
<ProductCard
56+
product={{
57+
name: 'Demo Product',
58+
price: 99.99,
59+
description:
60+
'This is a demo product to showcase the ProductCard component',
61+
image: 'https://via.placeholder.com/300',
62+
rating: 4.5,
63+
}}
64+
/>
65+
<ProductCard
66+
product={{
67+
name: 'Another Product',
68+
price: 149.99,
69+
description: 'Another demo product with different details',
70+
image: 'https://via.placeholder.com/300',
71+
rating: 5,
72+
}}
73+
/>
74+
</div>
75+
</section>
76+
77+
<section className="mb-8">
78+
<h2 className="mb-4 text-xl font-semibold">Interactive Components</h2>
79+
<div className="space-y-4">
80+
<div className="rounded border p-4">
81+
<h3 className="mb-2 font-medium">Count Up Animation</h3>
82+
<CountUp start={0} end={1000} />
83+
</div>
84+
<div className="rounded border p-4">
85+
<h3 className="mb-2 font-medium">Rendering Information</h3>
86+
<RenderingInfo />
87+
</div>
88+
</div>
89+
</section>
90+
</main>
91+
</div>
92+
);
93+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
2+
import { defineConfig } from 'cypress';
3+
4+
export default defineConfig({
5+
projectId: '27e40c91-5ac3-4433-8a87-651d10f51cf6',
6+
e2e: {
7+
...nxE2EPreset(__filename, { cypressDir: 'cypress' }),
8+
baseUrl: 'http://localhost:4001',
9+
},
10+
defaultCommandTimeout: 20000,
11+
retries: {
12+
runMode: 2,
13+
openMode: 1,
14+
},
15+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
describe('next-app-router-4001', () => {
2+
beforeEach(() => cy.visit('/'));
3+
4+
describe('Remote component (Button)', () => {
5+
it('should export Button component for module federation', () => {
6+
// This app serves as a remote that exports the Button component
7+
// Testing that the app loads successfully
8+
cy.visit('/');
9+
});
10+
});
11+
12+
describe('Button component functionality', () => {
13+
it('should render default button', () => {
14+
// If there's a test page that renders the button
15+
cy.visit('/');
16+
// Check if the page loads without errors
17+
cy.get('body').should('exist');
18+
});
19+
});
20+
21+
describe('Module Federation Remote', () => {
22+
it('should be accessible as a remote module', () => {
23+
// Verify the app is running and accessible
24+
cy.request('/').its('status').should('eq', 200);
25+
});
26+
});
27+
});

0 commit comments

Comments
 (0)