Skip to content

Commit 25c77d7

Browse files
klagridaclaude
andcommitted
test: fix E2E test isolation issues
This commit fixes two critical test isolation problems that were causing CI failures: 1. **Profile password tests modifying shared user**: The profile tests were updating Bob's password and attempting to reset it back to 'password123' at the end. If the reset failed or tests failed before reaching the reset, Bob's password remained changed, causing subsequent tests to fail with "Invalid login credentials". **Solution**: Created `createPasswordTestUser()` helper that generates a unique test user for each password modification test. This prevents Bob (shared test user) from being modified. 2. **Category/Post duplicate key errors**: Tests were creating categories and posts with hardcoded names/slugs, causing duplicate key violations when tests ran multiple times or in parallel. **Solution**: Created `getUniqueTestPost()` and `getUniqueTestCategory()` helpers that append timestamps to ensure unique values for each test run. These changes ensure tests are properly isolated and don't interfere with each other, which is critical for reliable CI/CD execution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d0d8201 commit 25c77d7

2 files changed

Lines changed: 74 additions & 43 deletions

File tree

frontend/e2e/posts.spec.ts

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,25 @@ const TEST_USER = {
55
password: 'password123',
66
};
77

8-
const TEST_POST = {
9-
title: 'Test Post Title',
10-
slug: 'test-post-slug',
11-
content: 'This is a test post content.',
12-
tags: 'test, playwright, e2e',
13-
};
14-
15-
const TEST_CATEGORY = {
16-
name: 'Test Category',
17-
slug: 'test-category',
18-
description: 'A test category for E2E testing',
19-
};
8+
// Helper to generate unique test data
9+
function getUniqueTestPost() {
10+
const timestamp = Date.now();
11+
return {
12+
title: `Test Post Title ${timestamp}`,
13+
slug: `test-post-slug-${timestamp}`,
14+
content: 'This is a test post content.',
15+
tags: 'test, playwright, e2e',
16+
};
17+
}
18+
19+
function getUniqueTestCategory() {
20+
const timestamp = Date.now();
21+
return {
22+
name: `Test Category ${timestamp}`,
23+
slug: `test-category-${timestamp}`,
24+
description: 'A test category for E2E testing',
25+
};
26+
}
2027

2128
test.describe('Posts Management', () => {
2229
test.beforeEach(async ({ page }) => {
@@ -53,11 +60,13 @@ test.describe('Posts Management', () => {
5360
test('should create a draft post', async ({ page }) => {
5461
await page.goto('/posts/create');
5562

63+
const testPost = getUniqueTestPost();
64+
5665
// Fill in post details
57-
await page.getByTestId('title-input').fill(TEST_POST.title);
58-
await page.getByTestId('slug-input').fill(TEST_POST.slug);
59-
await page.getByTestId('content-input').fill(TEST_POST.content);
60-
await page.getByTestId('tags-input').fill(TEST_POST.tags);
66+
await page.getByTestId('title-input').fill(testPost.title);
67+
await page.getByTestId('slug-input').fill(testPost.slug);
68+
await page.getByTestId('content-input').fill(testPost.content);
69+
await page.getByTestId('tags-input').fill(testPost.tags);
6170
await page.getByTestId('status-select').selectOption('draft');
6271

6372
// Submit
@@ -70,7 +79,7 @@ test.describe('Posts Management', () => {
7079
await expect(page).toHaveURL('/posts');
7180

7281
// Verify post appears in list
73-
await expect(page.locator(`text=${TEST_POST.title}`)).toBeVisible();
82+
await expect(page.locator(`text=${testPost.title}`)).toBeVisible();
7483
});
7584

7685
test('should create a published post', async ({ page }) => {
@@ -252,15 +261,17 @@ test.describe('Categories Management', () => {
252261
test('should create a new category', async ({ page }) => {
253262
await page.goto('/categories');
254263

255-
await page.getByTestId('name-input').fill(TEST_CATEGORY.name);
256-
await page.getByTestId('slug-input').fill(TEST_CATEGORY.slug);
257-
await page.getByTestId('description-input').fill(TEST_CATEGORY.description);
264+
const testCategory = getUniqueTestCategory();
265+
266+
await page.getByTestId('name-input').fill(testCategory.name);
267+
await page.getByTestId('slug-input').fill(testCategory.slug);
268+
await page.getByTestId('description-input').fill(testCategory.description);
258269
await page.getByTestId('create-button').click();
259270

260271
await expect(page.getByTestId('success-message')).toContainText(
261272
'Category created successfully'
262273
);
263-
await expect(page.locator(`text=${TEST_CATEGORY.name}`)).toBeVisible();
274+
await expect(page.locator(`text=${testCategory.name}`)).toBeVisible();
264275
});
265276

266277
test('should delete a category', async ({ page }) => {
@@ -317,10 +328,11 @@ test.describe('Posts with Categories', () => {
317328
await page.getByTestId('submit-button').click();
318329
await expect(page.getByTestId('dashboard-title')).toBeVisible();
319330

320-
// Create a test category first
331+
// Create a test category first (unique per test run)
332+
const testCategory = getUniqueTestCategory();
321333
await page.goto('/categories');
322-
await page.getByTestId('name-input').fill('E2E Category');
323-
await page.getByTestId('slug-input').fill('e2e-category');
334+
await page.getByTestId('name-input').fill(testCategory.name);
335+
await page.getByTestId('slug-input').fill(testCategory.slug);
324336
await page.getByTestId('create-button').click();
325337
await expect(page.getByTestId('success-message')).toBeVisible();
326338
});

frontend/e2e/profile.spec.ts

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,33 @@ test.describe('User Profile', () => {
1212
await expect(page).toHaveURL('/dashboard', { timeout: 5000 });
1313
});
1414

15+
// Helper to create a dedicated user for password update tests
16+
async function createPasswordTestUser(page: any): Promise<{ email: string; password: string }> {
17+
const timestamp = Date.now();
18+
const email = `pwdtest${timestamp}@example.com`;
19+
const password = 'password123';
20+
21+
// Logout current user
22+
await page.getByTestId('signout-button').click();
23+
await expect(page).toHaveURL('/login', { timeout: 5000 });
24+
25+
// Create new user
26+
await page.goto('/signup');
27+
await page.getByTestId('email-input').fill(email);
28+
await page.getByTestId('password-input').fill(password);
29+
await page.getByTestId('confirm-password-input').fill(password);
30+
await page.getByTestId('submit-button').click();
31+
await expect(page).toHaveURL('/login', { timeout: 5000 });
32+
33+
// Login with new user
34+
await page.getByTestId('email-input').fill(email);
35+
await page.getByTestId('password-input').fill(password);
36+
await page.getByTestId('submit-button').click();
37+
await expect(page).toHaveURL('/dashboard', { timeout: 5000 });
38+
39+
return { email, password };
40+
}
41+
1542
test('should display edit profile link in dashboard', async ({ page }) => {
1643
await expect(page.getByTestId('profile-link')).toBeVisible();
1744
await expect(page.getByTestId('profile-link')).toContainText('Edit Profile');
@@ -57,6 +84,9 @@ test.describe('User Profile', () => {
5784
});
5885

5986
test('should update password', async ({ page }) => {
87+
// Create dedicated user for password testing (avoid modifying shared test user)
88+
const user = await createPasswordTestUser(page);
89+
6090
await page.goto('/profile');
6191
await expect(page.getByTestId('loading')).not.toBeVisible({ timeout: 5000 });
6292

@@ -80,19 +110,11 @@ test.describe('User Profile', () => {
80110
await expect(page).toHaveURL('/login', { timeout: 5000 });
81111

82112
// Login with new password
83-
await page.getByTestId('email-input').fill('bob@example.com');
113+
await page.getByTestId('email-input').fill(user.email);
84114
await page.getByTestId('password-input').fill(newPassword);
85115
await page.getByTestId('submit-button').click();
86116

87117
await expect(page).toHaveURL('/dashboard', { timeout: 5000 });
88-
89-
// Reset password back to original for other tests
90-
await page.goto('/profile');
91-
await expect(page.getByTestId('loading')).not.toBeVisible({ timeout: 5000 });
92-
await page.getByTestId('password-input').fill('password123');
93-
await page.getByTestId('confirm-password-input').fill('password123');
94-
await page.getByTestId('submit-button').click();
95-
await expect(page).toHaveURL('/dashboard', { timeout: 3000 });
96118
});
97119

98120
test('should show password mismatch error', async ({ page }) => {
@@ -157,15 +179,18 @@ test.describe('User Profile', () => {
157179
});
158180

159181
test('should update profile with both username and password', async ({ page }) => {
182+
// Create dedicated user for password testing (avoid modifying shared test user)
183+
const user = await createPasswordTestUser(page);
184+
160185
await page.goto('/profile');
161186
await expect(page.getByTestId('loading')).not.toBeVisible({ timeout: 5000 });
162187

163188
const timestamp = Date.now();
164189
const newPassword = `temppass${timestamp}`;
165190

166191
// Update username, full name, and password
167-
await page.getByTestId('username-input').fill(`bob_full_${timestamp}`);
168-
await page.getByTestId('fullname-input').fill('Bob Complete Update');
192+
await page.getByTestId('username-input').fill(`testuser_full_${timestamp}`);
193+
await page.getByTestId('fullname-input').fill('Test User Complete Update');
169194
await page.getByTestId('password-input').fill(newPassword);
170195
await page.getByTestId('confirm-password-input').fill(newPassword);
171196

@@ -176,18 +201,12 @@ test.describe('User Profile', () => {
176201
await expect(page.getByTestId('success-message')).toBeVisible();
177202
await expect(page).toHaveURL('/dashboard', { timeout: 3000 });
178203

179-
// Reset password back for other tests
204+
// Verify can login with new password
180205
await page.getByTestId('signout-button').click();
181-
await page.getByTestId('email-input').fill('bob@example.com');
206+
await page.getByTestId('email-input').fill(user.email);
182207
await page.getByTestId('password-input').fill(newPassword);
183208
await page.getByTestId('submit-button').click();
184209
await expect(page).toHaveURL('/dashboard', { timeout: 5000 });
185-
186-
await page.goto('/profile');
187-
await expect(page.getByTestId('loading')).not.toBeVisible({ timeout: 5000 });
188-
await page.getByTestId('password-input').fill('password123');
189-
await page.getByTestId('confirm-password-input').fill('password123');
190-
await page.getByTestId('submit-button').click();
191210
});
192211

193212
test('should require authentication to access profile page', async ({ page }) => {

0 commit comments

Comments
 (0)