Skip to content

Commit 2771335

Browse files
Merge branch 'main' into json-manifest
# Conflicts: # examples/epic-stack/app/routes/_auth+/login.tsx # examples/federation/epic-stack-remote/rsbuild.config.ts # examples/federation/epic-stack/rsbuild.config.ts # examples/federation/epic-stack/tests/e2e/search.test.ts # examples/federation/epic-stack/tests/e2e/settings-profile.test.ts # pnpm-lock.yaml
2 parents e698f58 + f0082d8 commit 2771335

File tree

19 files changed

+453
-102
lines changed

19 files changed

+453
-102
lines changed

.github/workflows/e2e-tests.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: E2E Tests
1+
name: Build Test
22

33
on:
44
push:
@@ -7,7 +7,7 @@ on:
77
branches: [ main ]
88

99
jobs:
10-
e2e-tests:
10+
build-test:
1111
runs-on: ubuntu-latest
1212

1313
steps:
@@ -40,6 +40,12 @@ jobs:
4040
4141
- name: Install dependencies
4242
run: pnpm install
43+
44+
- name: Build package
45+
run: pnpm build
46+
47+
- name: Run publint
48+
run: npx publint --errors-only
4349

4450
- name: Install Playwright browsers
4551
run: npx playwright install --with-deps chromium

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export default defineConfig(() => {
4141
pluginReactRouter({
4242
// Optional: Enable custom server mode
4343
customServer: false,
44+
// Optional: Specify server output format
45+
serverOutput: "commonjs",
46+
//Optional: enable experimental support for module federation
47+
federation: false
4448
}),
4549
pluginReact()
4650
],
@@ -60,7 +64,19 @@ pluginReactRouter({
6064
* Enable this when you want to handle server setup manually.
6165
* @default false
6266
*/
63-
customServer?: boolean
67+
customServer?: boolean,
68+
69+
/**
70+
* Specify the output format for server-side code.
71+
* Options: "commonjs" | "module"
72+
* @default "module"
73+
*/
74+
serverOutput?: "commonjs" | "module"
75+
/**
76+
* Enable experimental support for module federation
77+
* @default false
78+
*/
79+
federation?: boolean
6480
})
6581
```
6682

examples/custom-node-server/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"dev": "RSDOCTOR=false node server.js",
99
"start": "NODE_ENV=production node server.js",
1010
"build": "rsbuild build",
11-
"typecheck": "react-router typegen && tsc"
11+
"typecheck": "react-router typegen && tsc",
12+
"test:e2e": "pnpm run dev & sleep 5 && playwright test",
13+
"test:e2e:debug": "playwright test --debug",
14+
"test:e2e:ui": "playwright test --ui"
1215
},
1316
"keywords": [],
1417
"author": "",
@@ -23,6 +26,7 @@
2326
"react-router": "^7.4.0"
2427
},
2528
"devDependencies": {
29+
"@playwright/test": "^1.50.1",
2630
"@react-router/dev": "^7.4.0",
2731
"@rsbuild/core": "^1.2.19",
2832
"@rsbuild/plugin-react": "^1.1.1",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
export default defineConfig({
4+
testDir: './tests/e2e',
5+
// Maximum time one test can run for
6+
timeout: 30 * 1000,
7+
expect: {
8+
timeout: 5000
9+
},
10+
// Run tests in files in parallel
11+
fullyParallel: false,
12+
// Fail the build on CI if you accidentally left test.only in the source code
13+
forbidOnly: !!process.env.CI,
14+
// Retry on CI only
15+
retries: process.env.CI ? 2 : 0,
16+
17+
// Shared settings for all the projects below
18+
use: {
19+
// Base URL to use in actions like `await page.goto('/')`
20+
baseURL: 'http://localhost:3000',
21+
22+
// Collect trace when retrying the failed test
23+
trace: 'on-first-retry',
24+
25+
// Take screenshot on test failure
26+
screenshot: 'only-on-failure',
27+
},
28+
29+
// Configure only Chrome desktop browser
30+
projects: [
31+
{
32+
name: 'chromium',
33+
use: { ...devices['Desktop Chrome'] },
34+
},
35+
]
36+
});

examples/custom-node-server/server/app.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

examples/custom-node-server/server/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ declare module 'react-router' {
77
}
88
}
99

10-
export const index = createRequestHandler({
10+
export const app = createRequestHandler({
1111
// @ts-expect-error - virtual module provided by React Router at build time
1212
build: () => import('virtual/react-router/server-build'),
1313
getLoadContext() {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"status": "passed",
3+
"failedTests": []
4+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# End-to-End Tests
2+
3+
This directory contains end-to-end tests for the React Router default template application using Playwright.
4+
5+
## Test Structure
6+
7+
The tests are organized by feature area:
8+
9+
- `home.test.ts` - Tests for the home page and welcome component
10+
- `about.test.ts` - Tests for the about page
11+
- `docs.test.ts` - Tests for the docs section with nested routes
12+
- `projects.test.ts` - Tests for the projects section with dynamic routes
13+
- `navigation.test.ts` - General navigation flows across the application
14+
15+
## Running Tests
16+
17+
You can run the tests using the following npm scripts:
18+
19+
```bash
20+
# Run all tests
21+
npm run test:e2e
22+
23+
# Run tests with the Playwright UI
24+
npm run test:e2e:ui
25+
26+
# Run tests in debug mode
27+
npm run test:e2e:debug
28+
```
29+
30+
## Test Configuration
31+
32+
Test configuration is defined in `playwright.config.ts` in the project root. The configuration:
33+
34+
- Runs tests in the `tests/e2e` directory
35+
- Tests across multiple browsers (Chrome, Firefox, Safari)
36+
- Tests across desktop and mobile viewports
37+
- Automatically starts the development server before running tests
38+
- Takes screenshots on test failures
39+
- Generates HTML reports
40+
41+
## Adding New Tests
42+
43+
To add new tests:
44+
45+
1. Create a new file in the `tests/e2e` directory with the `.test.ts` extension
46+
2. Import the required Playwright utilities:
47+
```typescript
48+
import { test, expect } from '@playwright/test';
49+
```
50+
3. Write your tests using the Playwright API
51+
4. Run your tests with `npm run test:e2e`
52+
53+
## Generating Base Screenshots
54+
55+
If you need to generate baseline screenshots for visual comparison:
56+
57+
```bash
58+
npx playwright test --update-snapshots
59+
```
60+
61+
## CI Integration
62+
63+
These tests can be integrated into CI pipelines. The configuration includes special settings for CI environments:
64+
65+
- More retries on CI
66+
- Forbidding `.only` tests on CI
67+
- Not reusing existing servers on CI
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('About Page', () => {
4+
test('should display about page content and team members', async ({ page }) => {
5+
// Navigate to about page
6+
await page.goto('/about');
7+
8+
// Check page heading
9+
const heading = page.locator('h1:has-text("About This Demo")');
10+
await expect(heading).toBeVisible();
11+
12+
// Check team member cards
13+
const teamCards = page.locator('.card');
14+
await expect(teamCards).toHaveCount(3);
15+
16+
// Verify each team member
17+
const expectedMembers = ['React Router', 'Tailwind CSS', 'TypeScript'];
18+
for (let i = 0; i < expectedMembers.length; i++) {
19+
const memberName = expectedMembers[i];
20+
await expect(teamCards.nth(i).locator('h2')).toContainText(memberName);
21+
}
22+
23+
// Check that back to home link works
24+
const backLink = page.locator('a:has-text("← Back to Home")');
25+
await expect(backLink).toBeVisible();
26+
await backLink.click();
27+
28+
// Verify navigation back to home page
29+
await expect(page).toHaveURL(/\/$/);
30+
await expect(page.locator('h1:has-text("Welcome to React Router")')).toBeVisible();
31+
});
32+
33+
test('should have working external links', async ({ page }) => {
34+
// Navigate to about page
35+
await page.goto('/about');
36+
37+
// Get all external links
38+
const externalLinks = page.locator('.card a[target="_blank"]');
39+
40+
// Verify each link has correct attributes
41+
for (const link of await externalLinks.all()) {
42+
await expect(link).toHaveAttribute('rel', 'noopener noreferrer');
43+
await expect(link).toHaveText('Learn more →');
44+
}
45+
});
46+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Docs Section', () => {
4+
test('should navigate through docs section with nested routes', async ({ page }) => {
5+
// Navigate to docs index
6+
await page.goto('/docs');
7+
8+
// Verify the docs index page is shown
9+
await expect(page).toHaveURL('/docs');
10+
11+
// Navigate to getting-started page
12+
await page.goto('/docs/getting-started');
13+
await expect(page).toHaveURL('/docs/getting-started');
14+
15+
// Navigate to advanced page
16+
await page.goto('/docs/advanced');
17+
await expect(page).toHaveURL('/docs/advanced');
18+
19+
// Verify layouts are preserved during navigation
20+
await page.goto('/docs');
21+
22+
// Check for the main navigation menu
23+
const mainNav = page.locator('header nav');
24+
await expect(mainNav).toBeVisible();
25+
await expect(mainNav.locator('a[href="/docs"]')).toBeVisible();
26+
});
27+
28+
test('should preserve layout when navigating between nested routes', async ({ page }) => {
29+
// Start at docs index
30+
await page.goto('/docs');
31+
32+
// Click on the Documentation link in the main nav
33+
const mainNav = page.locator('header nav');
34+
const docsLink = mainNav.locator('a[href="/docs"]');
35+
await expect(docsLink).toBeVisible();
36+
await expect(docsLink).toHaveAttribute('aria-current', 'page');
37+
38+
// Navigate to getting-started
39+
await page.goto('/docs/getting-started');
40+
await expect(page).toHaveURL('/docs/getting-started');
41+
42+
// The main navigation should still be visible
43+
await expect(mainNav).toBeVisible();
44+
await expect(docsLink).toBeVisible();
45+
46+
// Navigate to advanced
47+
await page.goto('/docs/advanced');
48+
await expect(page).toHaveURL('/docs/advanced');
49+
50+
// Navigation should still be preserved
51+
await expect(mainNav).toBeVisible();
52+
await expect(docsLink).toBeVisible();
53+
});
54+
});

0 commit comments

Comments
 (0)