Skip to content

Commit 5006a9b

Browse files
CopilotGeekTrainer
andcommitted
Create mock API server, update e2e tests for SSR, remove unused esbuild deps
Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com>
1 parent 7cfff68 commit 5006a9b

7 files changed

Lines changed: 2222 additions & 2569 deletions

File tree

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,47 @@
11
import { test, expect } from '@playwright/test';
22

33
test.describe('API Integration', () => {
4-
test('should fetch dogs from API', async ({ page }) => {
5-
// Mock successful API response
6-
await page.route('/api/dogs', route => {
7-
route.fulfill({
8-
status: 200,
9-
contentType: 'application/json',
10-
body: JSON.stringify([
11-
{ id: 1, name: 'Buddy', breed: 'Golden Retriever' },
12-
{ id: 2, name: 'Luna', breed: 'Husky' },
13-
{ id: 3, name: 'Max', breed: 'Labrador' }
14-
])
15-
});
16-
});
17-
4+
test('should render dogs from the mock API on the homepage', async ({ page }) => {
185
await page.goto('/');
19-
20-
// Check that mocked dogs are displayed
21-
await expect(page.getByText('Buddy')).toBeVisible();
22-
await expect(page.getByText('Golden Retriever')).toBeVisible();
23-
await expect(page.getByText('Luna')).toBeVisible();
24-
await expect(page.getByText('Husky')).toBeVisible();
25-
await expect(page.getByText('Max')).toBeVisible();
26-
await expect(page.getByText('Labrador')).toBeVisible();
6+
7+
const dogCards = page.getByTestId('dog-card');
8+
await expect(dogCards).toHaveCount(3);
9+
10+
await expect(page.getByTestId('dog-name').nth(0)).toHaveText('Buddy');
11+
await expect(page.getByTestId('dog-breed').nth(0)).toHaveText('Golden Retriever');
12+
13+
await expect(page.getByTestId('dog-name').nth(1)).toHaveText('Luna');
14+
await expect(page.getByTestId('dog-breed').nth(1)).toHaveText('Husky');
15+
16+
await expect(page.getByTestId('dog-name').nth(2)).toHaveText('Max');
17+
await expect(page.getByTestId('dog-breed').nth(2)).toHaveText('German Shepherd');
2718
});
2819

29-
test('should handle empty dog list', async ({ page }) => {
30-
// Mock empty API response
31-
await page.route('/api/dogs', route => {
32-
route.fulfill({
33-
status: 200,
34-
contentType: 'application/json',
35-
body: JSON.stringify([])
36-
});
37-
});
20+
test('should render dog details from the mock API', async ({ page }) => {
21+
await page.goto('/dog/1');
3822

39-
await page.goto('/');
40-
41-
// Check that empty state message is displayed
42-
await expect(page.getByText('No dogs available at the moment')).toBeVisible();
23+
await expect(page.getByTestId('dog-details')).toBeVisible();
24+
await expect(page.getByTestId('dog-name')).toHaveText('Buddy');
25+
await expect(page.getByTestId('dog-breed')).toContainText('Golden Retriever');
26+
await expect(page.getByTestId('dog-age')).toContainText('3');
27+
await expect(page.getByTestId('dog-gender')).toContainText('Male');
28+
await expect(page.getByTestId('dog-status')).toHaveText('Available');
4329
});
4430

45-
test('should handle network errors', async ({ page }) => {
46-
// Mock network error
47-
await page.route('/api/dogs', route => {
48-
route.abort('failed');
49-
});
31+
test('should return 404 details for non-existent dog', async ({ page }) => {
32+
await page.goto('/dog/99999');
33+
34+
await expect(page.getByTestId('error-message')).toBeVisible();
35+
await expect(page.getByTestId('error-message')).toContainText('not found');
36+
});
5037

38+
test('should link from dog card to detail page', async ({ page }) => {
5139
await page.goto('/');
52-
53-
// Check that error message is displayed
54-
await expect(page.getByText(/Error:/)).toBeVisible({ timeout: 10000 });
40+
41+
const firstCard = page.getByTestId('dog-card').first();
42+
await firstCard.click();
43+
44+
await expect(page).toHaveURL(/\/dog\/1$/);
45+
await expect(page.getByTestId('dog-details')).toBeVisible();
5546
});
56-
});
47+
});

client/e2e-tests/dog-details.spec.ts

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,44 @@ import { test, expect } from '@playwright/test';
33
test.describe('Dog Details', () => {
44
test('should navigate to dog details from homepage', async ({ page }) => {
55
await page.goto('/');
6-
7-
// Wait for dogs to load
8-
await page.waitForSelector('.grid a[href^="/dog/"]', { timeout: 10000 });
9-
10-
// Get the first dog link
11-
const firstDogLink = page.locator('.grid a[href^="/dog/"]').first();
12-
13-
// Get the dog name for verification
14-
const dogName = await firstDogLink.locator('h3').textContent();
15-
16-
// Click on the first dog
17-
await firstDogLink.click();
18-
19-
// Should be on a dog details page
20-
await expect(page.url()).toMatch(/\/dog\/\d+/);
21-
22-
// Check that the page title is correct
6+
7+
const firstDogCard = page.getByTestId('dog-card').first();
8+
const dogName = await page.getByTestId('dog-name').first().textContent();
9+
10+
await firstDogCard.click();
11+
12+
await expect(page).toHaveURL(/\/dog\/\d+/);
2313
await expect(page).toHaveTitle(/Dog Details - Tailspin Shelter/);
24-
25-
// Check for back button
26-
await expect(page.getByRole('link', { name: 'Back to All Dogs' })).toBeVisible();
14+
await expect(page.getByTestId('dog-details')).toBeVisible();
15+
await expect(page.getByTestId('dog-name')).toHaveText(dogName!);
16+
});
17+
18+
test('should display full dog details for Buddy', async ({ page }) => {
19+
await page.goto('/dog/1');
20+
21+
await expect(page.getByTestId('dog-details')).toBeVisible();
22+
await expect(page.getByTestId('dog-name')).toHaveText('Buddy');
23+
await expect(page.getByTestId('dog-breed')).toContainText('Golden Retriever');
24+
await expect(page.getByTestId('dog-age')).toContainText('3');
25+
await expect(page.getByTestId('dog-gender')).toContainText('Male');
26+
await expect(page.getByTestId('dog-status')).toHaveText('Available');
27+
await expect(page.getByTestId('dog-description')).toContainText('friendly and loyal');
2728
});
2829

2930
test('should navigate back to homepage from dog details', async ({ page }) => {
30-
// Go directly to a dog details page (assuming dog with ID 1 exists)
3131
await page.goto('/dog/1');
32-
33-
// Click the back button
34-
await page.getByRole('link', { name: 'Back to All Dogs' }).click();
35-
36-
// Should be redirected to homepage
32+
33+
await page.getByTestId('back-link').click();
34+
3735
await expect(page).toHaveURL('/');
3836
await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Shelter' })).toBeVisible();
3937
});
4038

4139
test('should handle invalid dog ID gracefully', async ({ page }) => {
42-
// Go to a dog page with an invalid ID
4340
await page.goto('/dog/99999');
44-
45-
// The page should still load (even if no dog is found)
41+
4642
await expect(page).toHaveTitle(/Dog Details - Tailspin Shelter/);
47-
48-
// Back button should still be available
49-
await expect(page.getByRole('link', { name: 'Back to All Dogs' })).toBeVisible();
43+
await expect(page.getByTestId('error-message')).toBeVisible();
44+
await expect(page.getByTestId('back-link')).toBeVisible();
5045
});
51-
});
46+
});

client/e2e-tests/homepage.spec.ts

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,36 @@ import { test, expect } from '@playwright/test';
33
test.describe('Tailspin Shelter Homepage', () => {
44
test('should load homepage and display title', async ({ page }) => {
55
await page.goto('/');
6-
7-
// Check that the page title is correct
6+
87
await expect(page).toHaveTitle(/Tailspin Shelter - Find Your Forever Friend/);
9-
10-
// Check that the main heading is visible
8+
119
await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Shelter' })).toBeVisible();
12-
13-
// Check that the description is visible
10+
1411
await expect(page.getByText('Find your perfect companion from our wonderful selection')).toBeVisible();
1512
});
1613

17-
test('should display dog list section', async ({ page }) => {
14+
test('should display dog list with mock data', async ({ page }) => {
1815
await page.goto('/');
19-
20-
// Check that the "Available Dogs" heading is visible
16+
2117
await expect(page.getByRole('heading', { name: 'Available Dogs' })).toBeVisible();
22-
23-
// Wait for dogs to load (either loading state, error, or actual dogs)
24-
await page.waitForSelector('.grid', { timeout: 10000 });
18+
19+
const dogList = page.getByTestId('dog-list');
20+
await expect(dogList).toBeVisible();
21+
22+
const dogCards = page.getByTestId('dog-card');
23+
await expect(dogCards).toHaveCount(3);
2524
});
2625

27-
test('should show loading state initially', async ({ page }) => {
26+
test('should display dog names and breeds from mock API', async ({ page }) => {
2827
await page.goto('/');
29-
30-
// Check that loading animation is shown initially
31-
// Look for the loading skeleton cards
32-
const loadingElements = page.locator('.animate-pulse').first();
33-
34-
// Either loading should be visible initially, or dogs should load quickly
35-
try {
36-
await expect(loadingElements).toBeVisible({ timeout: 2000 });
37-
} catch {
38-
// If loading finishes too quickly, that's fine - check for dog content instead
39-
await expect(page.locator('.grid')).toBeVisible();
40-
}
41-
});
4228

43-
test('should handle API errors gracefully', async ({ page }) => {
44-
// Intercept the API call and make it fail
45-
await page.route('/api/dogs', route => {
46-
route.fulfill({
47-
status: 500,
48-
contentType: 'application/json',
49-
body: JSON.stringify({ error: 'Internal Server Error' })
50-
});
51-
});
29+
await expect(page.getByTestId('dog-name').nth(0)).toHaveText('Buddy');
30+
await expect(page.getByTestId('dog-breed').nth(0)).toHaveText('Golden Retriever');
5231

53-
await page.goto('/');
54-
55-
// Check that error message is displayed
56-
await expect(page.getByText(/Failed to fetch data/)).toBeVisible({ timeout: 10000 });
32+
await expect(page.getByTestId('dog-name').nth(1)).toHaveText('Luna');
33+
await expect(page.getByTestId('dog-breed').nth(1)).toHaveText('Husky');
34+
35+
await expect(page.getByTestId('dog-name').nth(2)).toHaveText('Max');
36+
await expect(page.getByTestId('dog-breed').nth(2)).toHaveText('German Shepherd');
5737
});
58-
});
38+
});

client/e2e-tests/mock-api.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import http from 'node:http';
2+
3+
export const MOCK_PORT = 5199;
4+
5+
export const mockDogs = [
6+
{ id: 1, name: 'Buddy', breed: 'Golden Retriever' },
7+
{ id: 2, name: 'Luna', breed: 'Husky' },
8+
{ id: 3, name: 'Max', breed: 'German Shepherd' },
9+
];
10+
11+
export const mockDogDetails: Record<number, object> = {
12+
1: {
13+
id: 1,
14+
name: 'Buddy',
15+
breed: 'Golden Retriever',
16+
age: 3,
17+
description: 'A friendly and loyal companion who loves to play fetch.',
18+
gender: 'Male',
19+
status: 'AVAILABLE',
20+
},
21+
2: {
22+
id: 2,
23+
name: 'Luna',
24+
breed: 'Husky',
25+
age: 2,
26+
description: 'An energetic and playful dog who loves the outdoors.',
27+
gender: 'Female',
28+
status: 'PENDING',
29+
},
30+
3: {
31+
id: 3,
32+
name: 'Max',
33+
breed: 'German Shepherd',
34+
age: 5,
35+
description: 'A loyal and protective dog, great with families.',
36+
gender: 'Male',
37+
status: 'ADOPTED',
38+
},
39+
};
40+
41+
function handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
42+
res.setHeader('Content-Type', 'application/json');
43+
44+
const url = req.url || '';
45+
46+
// GET /api/dogs
47+
if (url === '/api/dogs' && req.method === 'GET') {
48+
res.writeHead(200);
49+
res.end(JSON.stringify(mockDogs));
50+
return;
51+
}
52+
53+
// GET /api/dogs/:id
54+
const dogMatch = url.match(/^\/api\/dogs\/(\d+)$/);
55+
if (dogMatch && req.method === 'GET') {
56+
const id = parseInt(dogMatch[1], 10);
57+
const dog = mockDogDetails[id];
58+
if (dog) {
59+
res.writeHead(200);
60+
res.end(JSON.stringify(dog));
61+
} else {
62+
res.writeHead(404);
63+
res.end(JSON.stringify({ error: 'Dog not found' }));
64+
}
65+
return;
66+
}
67+
68+
res.writeHead(404);
69+
res.end(JSON.stringify({ error: 'Not found' }));
70+
}
71+
72+
const server = http.createServer(handleRequest);
73+
74+
server.listen(MOCK_PORT, () => {
75+
console.log(`Mock API server running on http://localhost:${MOCK_PORT}`);
76+
});

0 commit comments

Comments
 (0)