import { test, expect } from '@playwright/test';
import { resetDatabase } from './helpers';
test.describe('Lifecycle Items Management Flow (@spec.md Decision 5, 7, 8, 11, 12, 19, 25, 28, 29)', () => {
test.beforeEach(async () => {
await resetDatabase();
});
test('should create a renewal item using the guided two-step form', async ({ page }) => {
await page.goto('/');
// Click Track Item in navbar
await page.getByRole('button', { name: /Track Item/i }).click();
// Step 1: Choose Lifecycle Type modal
const modal = page.locator('.fixed.inset-0').filter({ hasText: /Step [12]:/i });
await expect(modal).toBeVisible();
// Select Renewal
await modal.getByRole('button', { name: /Renewal/i }).click();
// Step 2: Enter common fields
await expect(page.locator('.fixed.inset-0').getByText(/Step 2: Track New/i)).toBeVisible();
const testItemName = 'E2E Motorcycle Registration';
await page.locator('#item-name').fill(testItemName);
// Set a future date (e.g., 20 days ahead)
const futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 20);
const dateStr = futureDate.toISOString().slice(0, 10);
await page.locator('#item-due-date').fill(dateStr);
// Submit form
await page.locator('.fixed.inset-0').getByRole('button', { name: /Track Item/i }).click();
await expect(modal).toBeHidden();
// Verify item appears on Dashboard in Upcoming section
await expect(page.locator(`text=${testItemName}`).first()).toBeVisible();
// Navigate to Renewals tab and verify item is listed
await page.getByRole('button', { name: 'Renewals' }).click();
await expect(page.locator(`text=${testItemName}`).first()).toBeVisible();
});
test('should create an item with missing due date (allowed per Decision 29)', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: /Track Item/i }).click();
const modal = page.locator('.fixed.inset-0').filter({ hasText: /Step [12]:/i });
await modal.getByRole('button', { name: /Warranty/i }).click();
const warrantyName = 'Vintage Mechanical Watch Warranty';
await page.locator('#item-name').fill(warrantyName);
// Uncheck "Has specific due date"
const checkbox = page.locator('.fixed.inset-0 input[type="checkbox"]');
await checkbox.uncheck();
await expect(page.locator('.fixed.inset-0').getByText(/No due date specified/i)).toBeVisible();
// Submit form
await page.locator('.fixed.inset-0').getByRole('button', { name: /Track Item/i }).click();
await expect(modal).toBeHidden();
// Navigate to Warranties tab
await page.getByRole('button', { name: 'Warranties' }).click();
const itemRow = page.locator('.group').filter({ hasText: warrantyName });
await expect(itemRow).toBeVisible();
await expect(itemRow.getByText(/No due date/i)).toBeVisible();
});
test('should toggle item completion and retain completed item in main list (Decision 28)', async ({ page }) => {
await page.goto('/');
// Find the overdue item "Vehicle Auto Insurance Policy"
const itemRow = page.locator('.group').filter({ hasText: 'Vehicle Auto Insurance Policy' }).first();
await expect(itemRow).toBeVisible();
// Click completion toggle button
const toggleBtn = itemRow.getByRole('button', { name: /Mark as complete/i });
await toggleBtn.click();
// Item should now be marked complete (line-through style, check icon)
await expect(itemRow.getByRole('button', { name: /Mark as active/i })).toBeVisible();
await expect(itemRow.locator('span.line-through')).toContainText('Vehicle Auto Insurance Policy');
// Per MVP Decision 28: Item remains visible in the list!
await expect(itemRow).toBeVisible();
// Toggle back to active
await itemRow.getByRole('button', { name: /Mark as active/i }).click();
await expect(itemRow.getByRole('button', { name: /Mark as complete/i })).toBeVisible();
});
test('should edit an existing item via edit modal', async ({ page }) => {
await page.goto('/');
const itemRow = page.locator('.group').filter({ hasText: 'International Passport Renewal' }).first();
await expect(itemRow).toBeVisible();
// Click edit button
await itemRow.getByRole('button', { name: /Edit item/i }).click();
const editModal = page.locator('.fixed.inset-0').filter({ hasText: /Edit Lifecycle Item/i });
await expect(editModal).toBeVisible();
// Change name
const updatedName = 'International Passport Renewal (Expedited)';
await editModal.locator('#edit-name').fill(updatedName);
await editModal.getByRole('button', { name: /Save Changes/i }).click();
await expect(editModal).toBeHidden();
await expect(page.locator(`text=${updatedName}`).first()).toBeVisible();
});
test('should permanently delete an item (Decision 19)', async ({ page }) => {
await page.goto('/');
const itemName = 'International Passport Renewal';
const itemRow = page.locator('.group').filter({ hasText: itemName }).first();
await expect(itemRow).toBeVisible();
// Accept window confirmation dialog
page.once('dialog', async (dialog) => {
await dialog.accept();
});
// Click delete
await itemRow.getByRole('button', { name: /Permanently delete item/i }).click();
// Verify item is permanently removed
await expect(page.locator('.group').filter({ hasText: itemName })).toHaveCount(0);
});
test('should filter dashboard items by scope (All vs Personal vs Group)', async ({ page }) => {
await page.goto('/');
const scopeSelect = page.locator('select').filter({ hasText: /All Accessible Items/i }).first();
await expect(scopeSelect).toBeVisible();
// Select Personal Only
await scopeSelect.selectOption('PERSONAL');
// Group items like "Oakridge Family & Home" should not appear with group badge
await expect(page.locator('.group').filter({ hasText: 'Shared Vehicle Maintenance' })).toHaveCount(0);
// Select Shared Vehicle Maintenance group
await scopeSelect.selectOption({ label: 'Group: Shared Vehicle Maintenance' });
await expect(page.locator('.group').filter({ hasText: 'Vehicle Auto Insurance Policy' }).first()).toBeVisible();
});
});