mitlist/fe/e2e/groups.spec.ts
google-labs-jules[bot] 0bf7a7cb49 feat: Add comprehensive unit and E2E tests for Vue frontend
This commit introduces a suite of unit and E2E tests for the Vue.js
frontend, significantly improving code coverage and reliability.

Unit Test Summary:
- Setup: Configured Vitest and @vue/test-utils.
- Core UI Components: Added tests for EssentialLink, SocialLoginButtons,
  and NotificationDisplay.
- Pinia Stores: Implemented tests for auth, notifications, and offline
  stores, including detailed testing of actions, getters, and state
  management. Offline store tests were adapted to its event-driven design.
- Services:
  - api.ts: Tested Axios client config, interceptors (auth token refresh),
    and wrapper methods.
  - choreService.ts & groupService.ts: Tested all existing service
    functions for CRUD operations, mocking API interactions.
- Pages:
  - AccountPage.vue: Tested rendering, data fetching, form submissions
    (profile, password, preferences), and error handling.
  - ChoresPage.vue: Tested rendering, chore display (personal & grouped),
    CRUD modals, and state handling (loading, error, empty).
  - LoginPage.vue: Verified existing comprehensive tests.

E2E Test (Playwright) Summary:
- Auth (`auth.spec.ts`):
  - User signup, login, and logout flows.
  - Logout test updated with correct UI selectors.
- Group Management (`groups.spec.ts`):
  - User login handled via `beforeAll` and `storageState`.
  - Create group and view group details.
  - Update and Delete group tests are skipped as corresponding UI
    functionality is not present in GroupDetailPage.vue.
  - Selectors updated based on component code.
- List Management (`lists.spec.ts`):
  - User login handled similarly.
  - Create list (within a group), view list, add item to list,
    and mark item as complete.
  - Delete list test is skipped as corresponding UI functionality
    is not present.
  - Selectors based on component code.

This work establishes a strong testing foundation for the frontend.
Skipped E2E tests highlight areas where UI functionality for certain
CRUD operations (group update/delete, list delete) may need to be added
if desired.
2025-05-22 06:41:35 +00:00

124 lines
7.2 KiB
TypeScript

import { test, expect, Page } from '@playwright/test';
const BASE_URL = 'http://localhost:5173'; // Assuming Vite's default dev server URL
// Credentials - These should ideally come from a shared config or be set by a global setup.
// For this example, we'll assume the user from auth.spec.ts exists or we use a known test user.
// If auth.spec.ts is guaranteed to run first and set a global userEmail, that could be used.
// For robustness, let's define specific credentials for this test suite, assuming this user exists.
// Or, better, use a dynamic user from auth.spec.ts if possible or a global setup.
// For now, hardcoding for clarity, but this implies this user MUST exist.
const userEmailForGroupTests = `testuser_${process.env.PLAYWRIGHT_WORKER_INDEX || 0}@example.com`; // Make it somewhat unique per worker if run in parallel
const userPasswordForGroupTests = 'Password123!';
// Helper to generate unique group names
const generateUniqueGroupName = () => `Test Group ${Date.now()}`;
let currentGroupName = ''; // To store the name of the group created in the test
test.describe.configure({ mode: 'serial' }); // Run group tests serially
// --- Login before all tests in this suite ---
test.beforeAll(async ({ browser }) => {
// Create a new page context for login to avoid interference with test-specific contexts
const page = await browser.newPage();
await page.goto(`${BASE_URL}/auth/login`);
await page.locator('input#email').fill(userEmailForGroupTests);
await page.locator('input#password').fill(userPasswordForGroupTests);
await page.locator('form button[type="submit"]:has-text("Login")').click();
// Wait for navigation to a main page, indicating successful login
await page.waitForURL(new RegExp(`${BASE_URL}/(chores|groups|dashboard)?/?$`));
// Save storage state (cookies, localStorage) after login
// This state will be used by subsequent tests in this file.
await page.context().storageState({ path: `e2e/.auth/user-${process.env.PLAYWRIGHT_WORKER_INDEX || 0}.json` });
await page.close();
});
// Use the saved authentication state for all tests in this file
test.use({ storageState: `e2e/.auth/user-${process.env.PLAYWRIGHT_WORKER_INDEX || 0}.json` });
test('1. Create a New Group', async ({ page }) => {
currentGroupName = generateUniqueGroupName();
await page.goto(`${BASE_URL}/groups`); // Assuming /groups is the main groups page
// Updated "Create New Group" button selector
const createGroupButton = page.getByRole('button', { name: 'Create New Group' })
.or(page.locator('.neo-create-group-card:has-text("+ Group")')); // This part remains the same as it's an OR condition
await createGroupButton.click();
// Updated modal input for group name selector
await page.locator('input#newGroupNameInput').fill(currentGroupName);
// Optionally fill description if available and required/tested
// await page.locator('textarea#group-description').fill('This is a test group description.');
// Updated modal submit button selector
await page.locator('.modal-footer').getByRole('button', { name: 'Create' }).click();
// Verify success notification (adjust selector for your notification component)
const successNotification = page.locator('.notification.success, .alert.alert-success, [data-testid="success-notification"]');
await expect(successNotification).toBeVisible({ timeout: 10000 });
await expect(successNotification).toContainText(/Group created successfully|Group saved successfully/i);
// Verify that the new group appears in the list of groups on the page
// Adjust selector for group items and how group name is displayed
await expect(page.locator(`:text("${currentGroupName}")`).first()).toBeVisible();
// More specific: await expect(page.locator(`.group-list-item:has-text("${currentGroupName}")`)).toBeVisible();
});
test('2. View Group Details', async ({ page }) => {
await page.goto(`${BASE_URL}/groups`);
// Click on the group created in the previous test to navigate to its detail page
// Updated group card selector (using :has-text for specificity) and group name header (h1.neo-group-header)
const groupCard = page.locator(`.neo-group-card:has-text("${currentGroupName}")`);
// The h1.neo-group-header is inside the card, so this is for verification if needed, not for clicking the card.
// For clicking, the groupCard selector itself is usually sufficient if the card is clickable.
await groupCard.click();
// Verify redirection to the group detail page (URL might be like /groups/some-id)
await page.waitForURL(new RegExp(`${BASE_URL}/groups/\\d+`)); // \d+ matches one or more digits for ID
// Verify that the group name is displayed on the detail page
// Updated group name display selector on GroupDetailPage.vue
const groupNameDisplay = page.locator('main h1'); // This was already good and specific enough
await expect(groupNameDisplay.first()).toContainText(currentGroupName);
// (Optional) Verify other elements like member list or chore list if applicable
// await expect(page.locator('.member-list')).toBeVisible();
});
// No changes needed for these skipped tests as per the analysis that UI doesn't exist.
// The existing console.warn messages are appropriate.
test.skip('3. Update Group Name', async ({ page }) => { // Intentionally skipped
// Reason: UI elements for editing group name/description (e.g., an "Edit Group" button
// or editable fields) are not present on the GroupDetailPage.vue based on prior file inspection.
// If these features are added, this test should be implemented.
console.warn('Skipping test "3. Update Group Name": UI for editing group details not found on GroupDetailPage.vue.');
// Placeholder for future implementation:
// await page.goto(`${BASE_URL}/groups`);
// await page.locator(`.neo-group-card:has-text("${currentGroupName}")`).click();
// await page.waitForURL(new RegExp(`${BASE_URL}/groups/\\d+`));
// await page.locator('button:has-text("Edit Group")').click(); // Assuming an edit button
// const updatedGroupName = `${currentGroupName} - Updated`;
// await page.locator('input#groupNameModalInput').fill(updatedGroupName); // Assuming modal input
// await page.locator('button:has-text("Save Changes")').click(); // Assuming save button
// await expect(page.locator('main h1').first()).toContainText(updatedGroupName);
// currentGroupName = updatedGroupName;
});
test.skip('4. Delete a Group', async ({ page }) => { // Intentionally skipped
// Reason: UI element for deleting an entire group (e.g., a "Delete Group" button)
// is not present on the GroupDetailPage.vue based on prior file inspection.
// If this feature is added, this test should be implemented.
console.warn('Skipping test "4. Delete a Group": UI for deleting group not found on GroupDetailPage.vue.');
// Placeholder for future implementation:
// await page.goto(`${BASE_URL}/groups`);
// await page.locator(`.neo-group-card:has-text("${currentGroupName}")`).click();
// await page.waitForURL(new RegExp(`${BASE_URL}/groups/\\d+`));
// page.on('dialog', dialog => dialog.accept()); // Handle confirmation dialog
// await page.locator('button:has-text("Delete Group")').click(); // Assuming a delete button
// await page.waitForURL(`${BASE_URL}/groups`);
// await expect(page.locator(`.neo-group-card:has-text("${currentGroupName}")`)).not.toBeVisible();
});