Add end-to-end testing infrastructure for the application: - Implemented Playwright E2E test suite with 31 passing tests across authentication and feature workflows - Created mock API fixtures for testing without requiring backend/database - Added data-testid attributes to major React components (Login, Register, TaskList, Events, SocialFeed, Profile, Navbar) - Set up test fixtures with test images (profile-pic.jpg, test-image.jpg) - Configured playwright.config.js for multi-browser testing (Chromium, Firefox, Safari) Test Coverage: - Authentication flows (register, login, logout, protected routes) - Task management (view, complete, filter, search) - Social feed (view posts, create post, like, view comments) - Events (view, join/RSVP, filter, view details) - User profile (view profile, streets, badges, statistics) - Premium features page - Leaderboard and rankings - Map view 🤖 Generated with OpenCode Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
391 lines
10 KiB
JavaScript
391 lines
10 KiB
JavaScript
/**
|
|
* Mock API responses for E2E testing
|
|
*/
|
|
|
|
export const mockUsers = {
|
|
testUser: {
|
|
_id: '507f1f77bcf86cd799439011',
|
|
name: 'Test User',
|
|
email: 'test@example.com',
|
|
password: 'hashedPassword123',
|
|
points: 150,
|
|
isPremium: false,
|
|
adoptedStreets: [
|
|
{
|
|
_id: '507f1f77bcf86cd799439012',
|
|
name: 'Main Street',
|
|
status: 'adopted'
|
|
}
|
|
],
|
|
badges: ['First Street', 'Task Master'],
|
|
tasksCompleted: 5,
|
|
eventsAttended: 2,
|
|
createdAt: '2024-01-01T00:00:00Z'
|
|
},
|
|
premiumUser: {
|
|
_id: '507f1f77bcf86cd799439013',
|
|
name: 'Premium User',
|
|
email: 'premium@example.com',
|
|
password: 'hashedPassword456',
|
|
points: 500,
|
|
isPremium: true,
|
|
adoptedStreets: [
|
|
{
|
|
_id: '507f1f77bcf86cd799439014',
|
|
name: 'Oak Avenue',
|
|
status: 'adopted'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439015',
|
|
name: 'Pine Street',
|
|
status: 'adopted'
|
|
}
|
|
],
|
|
badges: ['First Street', 'Task Master', 'Event Organizer', 'Maintenance Pro'],
|
|
tasksCompleted: 25,
|
|
eventsAttended: 10,
|
|
createdAt: '2023-01-01T00:00:00Z'
|
|
}
|
|
};
|
|
|
|
export const mockAuthToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoiNTA3ZjFmNzdiY2Y4NmNkNzk5NDM5MDExIn0sImlhdCI6MTcwMDAwMDAwMH0.mock_token';
|
|
|
|
export const mockTasks = [
|
|
{
|
|
_id: '507f1f77bcf86cd799439020',
|
|
description: 'Sweep the sidewalk on Main Street',
|
|
street: { _id: '507f1f77bcf86cd799439012', name: 'Main Street' },
|
|
status: 'pending',
|
|
priority: 'high',
|
|
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
assignedTo: null,
|
|
createdAt: '2024-01-01T00:00:00Z'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439021',
|
|
description: 'Remove litter from Oak Avenue',
|
|
street: { _id: '507f1f77bcf86cd799439014', name: 'Oak Avenue' },
|
|
status: 'pending',
|
|
priority: 'medium',
|
|
dueDate: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(),
|
|
assignedTo: null,
|
|
createdAt: '2024-01-02T00:00:00Z'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439022',
|
|
description: 'Plant flowers on Pine Street',
|
|
street: { _id: '507f1f77bcf86cd799439015', name: 'Pine Street' },
|
|
status: 'completed',
|
|
priority: 'low',
|
|
dueDate: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
assignedTo: mockUsers.testUser,
|
|
createdAt: '2023-12-25T00:00:00Z'
|
|
}
|
|
];
|
|
|
|
export const mockPosts = [
|
|
{
|
|
_id: '507f1f77bcf86cd799439030',
|
|
content: 'Just completed cleaning up Main Street! It looks amazing now.',
|
|
user: { _id: mockUsers.testUser._id, name: mockUsers.testUser.name },
|
|
likes: [
|
|
{ _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name }
|
|
],
|
|
comments: [
|
|
{
|
|
_id: '507f1f77bcf86cd799439031',
|
|
text: 'Great work!',
|
|
user: { _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name },
|
|
createdAt: '2024-01-01T12:00:00Z'
|
|
}
|
|
],
|
|
createdAt: '2024-01-01T10:00:00Z'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439032',
|
|
content: 'Looking for volunteers to help with tree planting on Pine Street next weekend!',
|
|
user: { _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name },
|
|
likes: [],
|
|
comments: [],
|
|
createdAt: '2024-01-02T08:00:00Z'
|
|
}
|
|
];
|
|
|
|
export const mockEvents = [
|
|
{
|
|
_id: '507f1f77bcf86cd799439040',
|
|
title: 'Community Clean-up Day',
|
|
description: 'Join us for a community-wide clean-up event on Main Street and Oak Avenue.',
|
|
date: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
location: 'Main Street & Oak Avenue',
|
|
organizer: { _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name },
|
|
participants: [
|
|
{ _id: mockUsers.testUser._id, name: mockUsers.testUser.name },
|
|
{ _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name }
|
|
],
|
|
status: 'upcoming',
|
|
createdAt: '2024-01-01T00:00:00Z'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439041',
|
|
title: 'Tree Planting Initiative',
|
|
description: 'Help us plant native trees throughout the neighborhood.',
|
|
date: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(),
|
|
location: 'Pine Street Park',
|
|
organizer: { _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name },
|
|
participants: [
|
|
{ _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name }
|
|
],
|
|
status: 'upcoming',
|
|
createdAt: '2024-01-02T00:00:00Z'
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439042',
|
|
title: 'Past Clean-up Event',
|
|
description: 'We cleaned up the neighborhood successfully!',
|
|
date: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
location: 'Downtown Area',
|
|
organizer: { _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name },
|
|
participants: [
|
|
{ _id: mockUsers.testUser._id, name: mockUsers.testUser.name },
|
|
{ _id: mockUsers.premiumUser._id, name: mockUsers.premiumUser.name }
|
|
],
|
|
status: 'past',
|
|
createdAt: '2023-12-20T00:00:00Z'
|
|
}
|
|
];
|
|
|
|
export const mockStreets = [
|
|
{
|
|
_id: '507f1f77bcf86cd799439012',
|
|
name: 'Main Street',
|
|
coordinates: [40.7128, -74.0060],
|
|
status: 'adopted',
|
|
adoptedBy: mockUsers.testUser,
|
|
lastMaintained: '2024-01-01T00:00:00Z',
|
|
maintenanceHistory: [
|
|
{
|
|
date: '2024-01-01T00:00:00Z',
|
|
type: 'cleanup',
|
|
completedBy: mockUsers.testUser
|
|
}
|
|
]
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439014',
|
|
name: 'Oak Avenue',
|
|
coordinates: [40.7138, -74.0070],
|
|
status: 'adopted',
|
|
adoptedBy: mockUsers.premiumUser,
|
|
lastMaintained: '2024-01-02T00:00:00Z',
|
|
maintenanceHistory: []
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439016',
|
|
name: 'Elm Street',
|
|
coordinates: [40.7118, -74.0050],
|
|
status: 'available',
|
|
adoptedBy: null,
|
|
lastMaintained: null,
|
|
maintenanceHistory: []
|
|
}
|
|
];
|
|
|
|
export const mockRewards = [
|
|
{
|
|
_id: '507f1f77bcf86cd799439050',
|
|
name: 'Coffee Gift Card',
|
|
points: 50,
|
|
description: 'A $5 gift card to your favorite coffee shop',
|
|
category: 'food',
|
|
available: true
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439051',
|
|
name: 'Movie Tickets',
|
|
points: 100,
|
|
description: 'Two free movie tickets',
|
|
category: 'entertainment',
|
|
available: true
|
|
},
|
|
{
|
|
_id: '507f1f77bcf86cd799439052',
|
|
name: 'Plant a Tree',
|
|
points: 150,
|
|
description: 'Sponsor a tree to be planted in the community',
|
|
category: 'charity',
|
|
available: true
|
|
}
|
|
];
|
|
|
|
export const mockLeaderboard = [
|
|
{
|
|
rank: 1,
|
|
user: mockUsers.premiumUser,
|
|
points: mockUsers.premiumUser.points,
|
|
tasksCompleted: 25,
|
|
eventsAttended: 10
|
|
},
|
|
{
|
|
rank: 2,
|
|
user: mockUsers.testUser,
|
|
points: mockUsers.testUser.points,
|
|
tasksCompleted: 5,
|
|
eventsAttended: 2
|
|
}
|
|
];
|
|
|
|
/**
|
|
* Setup mock API responses using MSW or intercept
|
|
*/
|
|
export async function setupMockApi(page) {
|
|
// Intercept API requests and return mock responses
|
|
await page.route('**/api/auth/register', async route => {
|
|
await route.abort();
|
|
});
|
|
|
|
await page.route('**/api/auth/login', async route => {
|
|
const request = route.request();
|
|
const postData = request.postDataJSON();
|
|
|
|
if (postData.email === 'test@example.com' && postData.password === 'password123') {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
token: mockAuthToken,
|
|
user: mockUsers.testUser
|
|
})
|
|
});
|
|
} else {
|
|
await route.fulfill({
|
|
status: 401,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: false,
|
|
msg: 'Invalid email or password'
|
|
})
|
|
});
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/auth/logout', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
msg: 'Logged out successfully'
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/users/**', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockUsers.testUser)
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/tasks', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockTasks)
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/tasks/**', async route => {
|
|
const method = route.request().method();
|
|
if (method === 'PUT') {
|
|
// Task completion
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ ...mockTasks[0], status: 'completed' })
|
|
});
|
|
} else {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockTasks[0])
|
|
});
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/posts', async route => {
|
|
const method = route.request().method();
|
|
if (method === 'POST') {
|
|
const newPost = {
|
|
_id: '507f1f77bcf86cd799439099',
|
|
content: (await route.request().postDataJSON()).content,
|
|
user: mockUsers.testUser,
|
|
likes: [],
|
|
comments: [],
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
await route.fulfill({
|
|
status: 201,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(newPost)
|
|
});
|
|
} else {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockPosts)
|
|
});
|
|
}
|
|
});
|
|
|
|
await page.route('**/api/posts/like/**', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([mockUsers.testUser])
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/events', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockEvents)
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/events/rsvp/**', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([mockUsers.testUser, mockUsers.premiumUser])
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/streets', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockStreets)
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/rewards', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockRewards)
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/leaderboard', async route => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockLeaderboard)
|
|
});
|
|
});
|
|
}
|