Skip to content

Commit 8497ef2

Browse files
chore: add test cases
1 parent dc36c30 commit 8497ef2

File tree

7 files changed

+353
-0
lines changed

7 files changed

+353
-0
lines changed
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+
});
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+
// This assumes there's a common layout element across all docs pages
21+
await page.goto('/docs');
22+
23+
// This is a placeholder - you may need to adjust this based on your actual layout structure
24+
// For example, if you have a sidebar that's part of the layout
25+
await expect(page.locator('nav, aside, .sidebar').first()).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 a link to getting-started (assuming there's a navigation menu in the layout)
33+
// Modify this locator based on your actual navigation structure
34+
const gettingStartedLink = page.locator('a[href="/docs/getting-started"]');
35+
if (await gettingStartedLink.isVisible()) {
36+
await gettingStartedLink.click();
37+
await expect(page).toHaveURL('/docs/getting-started');
38+
39+
// The layout should still be visible
40+
// Replace with your layout-specific selector
41+
await expect(page.locator('nav, aside, .sidebar').first()).toBeVisible();
42+
43+
// Now try the advanced page
44+
const advancedLink = page.locator('a[href="/docs/advanced"]');
45+
if (await advancedLink.isVisible()) {
46+
await advancedLink.click();
47+
await expect(page).toHaveURL('/docs/advanced');
48+
49+
// Layout should still be preserved
50+
await expect(page.locator('nav, aside, .sidebar').first()).toBeVisible();
51+
}
52+
}
53+
});
54+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Home Page', () => {
4+
test('should display welcome message and feature cards', async ({ page }) => {
5+
// Navigate to home page
6+
await page.goto('/');
7+
8+
// Check page title
9+
await expect(page).toHaveTitle(/React Router Demo/);
10+
11+
// Check welcome message
12+
const welcomeHeading = page.locator('h1:has-text("Welcome to React Router")');
13+
await expect(welcomeHeading).toBeVisible();
14+
15+
// Check feature cards (there should be 3)
16+
const featureCards = page.locator('.card:has(h2)').filter({ hasText: /Dynamic Routing|Nested Routes|Route Protection/ });
17+
await expect(featureCards).toHaveCount(3);
18+
19+
// Test hover state on a feature card
20+
const firstFeatureCard = featureCards.first();
21+
await firstFeatureCard.hover();
22+
await expect(firstFeatureCard).toHaveClass(/ring-2 ring-blue-500/);
23+
24+
// Test link to about page
25+
const aboutPageLink = page.locator('a.bg-blue-600').filter({ hasText: 'View About Page' });
26+
await expect(aboutPageLink).toBeVisible();
27+
await aboutPageLink.click();
28+
29+
// Verify navigation to about page
30+
await expect(page).toHaveURL(/\/about$/);
31+
await expect(page.locator('h1:has-text("About This Demo")')).toBeVisible();
32+
});
33+
34+
test('should have working resource links', async ({ page }) => {
35+
// Navigate to home page
36+
await page.goto('/');
37+
38+
// Check resource cards
39+
const resourceLinks = page.locator('a.card').filter({
40+
hasText: /React Router Documentation|GitHub Repository|React Router Blog/
41+
});
42+
await expect(resourceLinks).toHaveCount(3);
43+
44+
// Test that links have proper attributes
45+
for (const link of await resourceLinks.all()) {
46+
await expect(link).toHaveAttribute('target', '_blank');
47+
await expect(link).toHaveAttribute('rel', 'noreferrer');
48+
}
49+
});
50+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Navigation Flow', () => {
4+
test('should navigate through all major sections of the app', async ({ page }) => {
5+
// Start at the home page
6+
await page.goto('/');
7+
await expect(page).toHaveURL('/');
8+
9+
// Navigate to about page
10+
await page.goto('/about');
11+
await expect(page).toHaveURL('/about');
12+
13+
// Navigate to docs section
14+
await page.goto('/docs');
15+
await expect(page).toHaveURL('/docs');
16+
17+
// Navigate to projects section
18+
await page.goto('/projects');
19+
await expect(page).toHaveURL('/projects');
20+
21+
// Navigate to a specific project
22+
const projectId = 'react-router';
23+
await page.goto(`/projects/${projectId}`);
24+
await expect(page).toHaveURL(`/projects/${projectId}`);
25+
});
26+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.describe('Projects Section', () => {
4+
test('should display projects listing', async ({ page }) => {
5+
// Navigate to projects index
6+
await page.goto('/projects');
7+
8+
// Verify the projects index page URL
9+
await expect(page).toHaveURL('/projects');
10+
});
11+
12+
test('should navigate to project detail page', async ({ page }) => {
13+
// We'll test with a known project ID
14+
// Since this is a demo app, we can use 'react-router' as the ID
15+
const projectId = 'react-router';
16+
17+
// Go directly to the project page
18+
await page.goto(`/projects/${projectId}`);
19+
20+
// Verify we're on the correct page
21+
await expect(page).toHaveURL(`/projects/${projectId}`);
22+
23+
// Check project name is displayed
24+
const projectName = page.locator('h1').first();
25+
await expect(projectName).toBeVisible();
26+
27+
// Check edit and settings links
28+
const editLink = page.locator('a[href="edit"]');
29+
await expect(editLink).toBeVisible();
30+
31+
const settingsLink = page.locator('a[href="settings"]');
32+
await expect(settingsLink).toBeVisible();
33+
34+
// Check progress section
35+
const progressSection = page.locator('.card:has-text("Progress")');
36+
await expect(progressSection).toBeVisible();
37+
38+
// Check team section
39+
const teamSection = page.locator('.card:has-text("Team")');
40+
await expect(teamSection).toBeVisible();
41+
42+
// Check activity section
43+
const activitySection = page.locator('.card:has-text("Recent Activity")');
44+
await expect(activitySection).toBeVisible();
45+
});
46+
47+
test('should navigate to project edit page', async ({ page }) => {
48+
const projectId = 'react-router';
49+
50+
// Go to the project detail page
51+
await page.goto(`/projects/${projectId}`);
52+
53+
// Click the edit link
54+
const editLink = page.locator('a:has-text("Edit")');
55+
await editLink.click();
56+
57+
// Verify we're on the edit page
58+
await expect(page).toHaveURL(`/projects/${projectId}/edit`);
59+
});
60+
61+
test('should navigate to project settings page', async ({ page }) => {
62+
const projectId = 'react-router';
63+
64+
// Go to the project detail page
65+
await page.goto(`/projects/${projectId}`);
66+
67+
// Click the settings link
68+
const settingsLink = page.locator('a:has-text("Settings")');
69+
await settingsLink.click();
70+
71+
// Verify we're on the settings page
72+
await expect(page).toHaveURL(`/projects/${projectId}/settings`);
73+
});
74+
});

0 commit comments

Comments
 (0)