Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
80eb6c8
chore: federation
ScriptedAlchemy Feb 28, 2025
17f6736
fix: tests
ScriptedAlchemy Feb 28, 2025
35c5e24
chore: add federation
ScriptedAlchemy Mar 1, 2025
f164bfa
chore: add federation
ScriptedAlchemy Mar 10, 2025
24937f0
chore: add federation
ScriptedAlchemy Mar 11, 2025
851f076
chore: update rstack
ScriptedAlchemy Mar 18, 2025
53bcbd9
chore: update rstack
ScriptedAlchemy Mar 18, 2025
a529bc4
chore: update rstack
ScriptedAlchemy Mar 18, 2025
3bdd69e
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 19, 2025
24535bc
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 20, 2025
76738ea
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 20, 2025
003411d
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 21, 2025
7351e1e
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 21, 2025
78027d8
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 21, 2025
fccd2b4
chore: fix ssr in rr7.3
ScriptedAlchemy Mar 21, 2025
f0b66e8
Fix: Remove unused imports in manifest.ts to resolve TypeScript linte…
ScriptedAlchemy Mar 21, 2025
3cbed5d
chore: fix federation imports
ScriptedAlchemy Mar 23, 2025
a317ea0
Merge branch 'main' into federation
ScriptedAlchemy Mar 23, 2025
a7462ae
chore: upgrade react router
ScriptedAlchemy Mar 23, 2025
3730eb9
Merge branch 'main' into federation
ScriptedAlchemy Mar 23, 2025
c03da62
chore: stage over updates from mf branch
ScriptedAlchemy Mar 23, 2025
2420e5f
chore: add prettier
ScriptedAlchemy Mar 23, 2025
601cbcb
chore: add prettier
ScriptedAlchemy Mar 23, 2025
13d4940
chore: add prettier
ScriptedAlchemy Mar 23, 2025
140205c
fix: improve async handler logic in server-utils
ScriptedAlchemy Mar 23, 2025
b1dfc40
chore: add E2E testing workflow
ScriptedAlchemy Mar 23, 2025
fb6acf0
fix: update federation option to use dynamic configuration
ScriptedAlchemy Mar 23, 2025
a94980b
Merge branch 'main' into federation
ScriptedAlchemy Mar 24, 2025
b4f11c1
Merge branch 'main' into federation
ScriptedAlchemy Mar 24, 2025
efc2f8d
workaround e2e flakes, consume all mf modules
ScriptedAlchemy Mar 24, 2025
a43e2c5
workaround e2e flakes, consume all mf modules
ScriptedAlchemy Mar 24, 2025
bd8d074
fix custom server example
ScriptedAlchemy Mar 24, 2025
17626f3
update docs and examples
ScriptedAlchemy Mar 24, 2025
c2390a4
add tests
ScriptedAlchemy Mar 24, 2025
5c301c1
add tests
ScriptedAlchemy Mar 24, 2025
8165647
add tests
ScriptedAlchemy Mar 24, 2025
f9aed36
Update package.json repository URL, enhance e2e script, and rename Gi…
ScriptedAlchemy Mar 24, 2025
711d1a7
Update e2e script in package.json to remove redundant port kill comma…
ScriptedAlchemy Mar 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 8 additions & 2 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: E2E Tests
name: Build Test

on:
push:
Expand All @@ -7,7 +7,7 @@ on:
branches: [ main ]

jobs:
e2e-tests:
build-test:
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -40,6 +40,12 @@ jobs:

- name: Install dependencies
run: pnpm install

- name: Build package
run: pnpm build

- name: Run publint
run: npx publint --errors-only

- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
Expand Down
20 changes: 20 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Build artifacts
dist/
node_modules/
.git/
.vscode/
.idea/

# Examples directory (not our focus)
examples/

# Test files (only focusing on src)
tests/

# Coverage reports
coverage/

# Lock files
pnpm-lock.yaml
package-lock.json
yarn.lock
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 80,
"arrowParens": "avoid",
"endOfLine": "lf"
}
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export default defineConfig(() => {
pluginReactRouter({
// Optional: Enable custom server mode
customServer: false,
// Optional: Specify server output format
serverOutput: "commonjs",
//Optional: enable experimental support for module federation
federation: false
}),
pluginReact()
],
Expand All @@ -60,7 +64,19 @@ pluginReactRouter({
* Enable this when you want to handle server setup manually.
* @default false
*/
customServer?: boolean
customServer?: boolean,

/**
* Specify the output format for server-side code.
* Options: "commonjs" | "module"
* @default "module"
*/
serverOutput?: "commonjs" | "module"
/**
* Enable experimental support for module federation
* @default false
*/
federation?: boolean
})
```

Expand Down
4 changes: 2 additions & 2 deletions examples/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"@cloudflare/workers-types": "^4.20241112.0",
"@react-router/cloudflare": "^7.4.0",
"@react-router/dev": "^7.4.0",
"@rsbuild/core": "^1.2.3",
"@rsbuild/plugin-react": "^1.1.0",
"@rsbuild/core": "^1.2.19",
"@rsbuild/plugin-react": "^1.1.1",
"@rsbuild/plugin-react-router": "workspace:*",
"@tailwindcss/postcss": "^4.0.0",
"@types/node": "^20",
Expand Down
10 changes: 7 additions & 3 deletions examples/custom-node-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
"dev": "RSDOCTOR=false node server.js",
"start": "NODE_ENV=production node server.js",
"build": "rsbuild build",
"typecheck": "react-router typegen && tsc"
"typecheck": "react-router typegen && tsc",
"test:e2e": "pnpm run dev & sleep 5 && playwright test",
"test:e2e:debug": "playwright test --debug",
"test:e2e:ui": "playwright test --ui"
},
"keywords": [],
"author": "",
Expand All @@ -23,9 +26,10 @@
"react-router": "^7.4.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@react-router/dev": "^7.4.0",
"@rsbuild/core": "^1.2.3",
"@rsbuild/plugin-react": "^1.1.0",
"@rsbuild/core": "^1.2.19",
"@rsbuild/plugin-react": "^1.1.1",
"@rsbuild/plugin-react-router": "workspace:*",
"@rsdoctor/rspack-plugin": "^0.4.13",
"@types/express": "^5.0.0",
Expand Down
36 changes: 36 additions & 0 deletions examples/custom-node-server/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e',
// Maximum time one test can run for
timeout: 30 * 1000,
expect: {
timeout: 5000
},
// Run tests in files in parallel
fullyParallel: false,
// Fail the build on CI if you accidentally left test.only in the source code
forbidOnly: !!process.env.CI,
// Retry on CI only
retries: process.env.CI ? 2 : 0,

// Shared settings for all the projects below
use: {
// Base URL to use in actions like `await page.goto('/')`
baseURL: 'http://localhost:3000',

// Collect trace when retrying the failed test
trace: 'on-first-retry',

// Take screenshot on test failure
screenshot: 'only-on-failure',
},

// Configure only Chrome desktop browser
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
]
});
18 changes: 0 additions & 18 deletions examples/custom-node-server/server/app.ts

This file was deleted.

2 changes: 1 addition & 1 deletion examples/custom-node-server/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ declare module 'react-router' {
}
}

export const index = createRequestHandler({
export const app = createRequestHandler({
// @ts-expect-error - virtual module provided by React Router at build time
build: () => import('virtual/react-router/server-build'),
getLoadContext() {
Expand Down
4 changes: 4 additions & 0 deletions examples/custom-node-server/test-results/.last-run.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}
67 changes: 67 additions & 0 deletions examples/custom-node-server/tests/e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# End-to-End Tests

This directory contains end-to-end tests for the React Router default template application using Playwright.

## Test Structure

The tests are organized by feature area:

- `home.test.ts` - Tests for the home page and welcome component
- `about.test.ts` - Tests for the about page
- `docs.test.ts` - Tests for the docs section with nested routes
- `projects.test.ts` - Tests for the projects section with dynamic routes
- `navigation.test.ts` - General navigation flows across the application

## Running Tests

You can run the tests using the following npm scripts:

```bash
# Run all tests
npm run test:e2e

# Run tests with the Playwright UI
npm run test:e2e:ui

# Run tests in debug mode
npm run test:e2e:debug
```

## Test Configuration

Test configuration is defined in `playwright.config.ts` in the project root. The configuration:

- Runs tests in the `tests/e2e` directory
- Tests across multiple browsers (Chrome, Firefox, Safari)
- Tests across desktop and mobile viewports
- Automatically starts the development server before running tests
- Takes screenshots on test failures
- Generates HTML reports

## Adding New Tests

To add new tests:

1. Create a new file in the `tests/e2e` directory with the `.test.ts` extension
2. Import the required Playwright utilities:
```typescript
import { test, expect } from '@playwright/test';
```
3. Write your tests using the Playwright API
4. Run your tests with `npm run test:e2e`

## Generating Base Screenshots

If you need to generate baseline screenshots for visual comparison:

```bash
npx playwright test --update-snapshots
```

## CI Integration

These tests can be integrated into CI pipelines. The configuration includes special settings for CI environments:

- More retries on CI
- Forbidding `.only` tests on CI
- Not reusing existing servers on CI
46 changes: 46 additions & 0 deletions examples/custom-node-server/tests/e2e/about.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { test, expect } from '@playwright/test';

test.describe('About Page', () => {
test('should display about page content and team members', async ({ page }) => {
// Navigate to about page
await page.goto('/about');

// Check page heading
const heading = page.locator('h1:has-text("About This Demo")');
await expect(heading).toBeVisible();

// Check team member cards
const teamCards = page.locator('.card');
await expect(teamCards).toHaveCount(3);

// Verify each team member
const expectedMembers = ['React Router', 'Tailwind CSS', 'TypeScript'];
for (let i = 0; i < expectedMembers.length; i++) {
const memberName = expectedMembers[i];
await expect(teamCards.nth(i).locator('h2')).toContainText(memberName);
}

// Check that back to home link works
const backLink = page.locator('a:has-text("← Back to Home")');
await expect(backLink).toBeVisible();
await backLink.click();

// Verify navigation back to home page
await expect(page).toHaveURL(/\/$/);
await expect(page.locator('h1:has-text("Welcome to React Router")')).toBeVisible();
});

test('should have working external links', async ({ page }) => {
// Navigate to about page
await page.goto('/about');

// Get all external links
const externalLinks = page.locator('.card a[target="_blank"]');

// Verify each link has correct attributes
for (const link of await externalLinks.all()) {
await expect(link).toHaveAttribute('rel', 'noopener noreferrer');
await expect(link).toHaveText('Learn more →');
}
});
});
54 changes: 54 additions & 0 deletions examples/custom-node-server/tests/e2e/docs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { test, expect } from '@playwright/test';

test.describe('Docs Section', () => {
test('should navigate through docs section with nested routes', async ({ page }) => {
// Navigate to docs index
await page.goto('/docs');

// Verify the docs index page is shown
await expect(page).toHaveURL('/docs');

// Navigate to getting-started page
await page.goto('/docs/getting-started');
await expect(page).toHaveURL('/docs/getting-started');

// Navigate to advanced page
await page.goto('/docs/advanced');
await expect(page).toHaveURL('/docs/advanced');

// Verify layouts are preserved during navigation
await page.goto('/docs');

// Check for the main navigation menu
const mainNav = page.locator('header nav');
await expect(mainNav).toBeVisible();
await expect(mainNav.locator('a[href="/docs"]')).toBeVisible();
});

test('should preserve layout when navigating between nested routes', async ({ page }) => {
// Start at docs index
await page.goto('/docs');

// Click on the Documentation link in the main nav
const mainNav = page.locator('header nav');
const docsLink = mainNav.locator('a[href="/docs"]');
await expect(docsLink).toBeVisible();
await expect(docsLink).toHaveAttribute('aria-current', 'page');

// Navigate to getting-started
await page.goto('/docs/getting-started');
await expect(page).toHaveURL('/docs/getting-started');

// The main navigation should still be visible
await expect(mainNav).toBeVisible();
await expect(docsLink).toBeVisible();

// Navigate to advanced
await page.goto('/docs/advanced');
await expect(page).toHaveURL('/docs/advanced');

// Navigation should still be preserved
await expect(mainNav).toBeVisible();
await expect(docsLink).toBeVisible();
});
});
Loading