/** * End-to-End Contact Management Tests * * Comprehensive test suite for Time Safari's contact management and gift recording features. * Tests run sequentially to avoid state conflicts and API rate limits. * * Test Flow: * 1. Contact Creation & Verification * - Add contact using DID * - Verify contact appears in list * - Rename contact and verify change * - Check contact appears in "Record Something" section * * 2. Gift Recording Flow * - Generate unique gift details * - Record gift to contact * - Verify gift confirmation * - Check gift appears in activity feed * * 3. Contact Import/Export Tests * - Copy contact details to clipboard * - Delete existing contact * - Import contact from clipboard * - Verify imported contact details * * Test Data Generation: * - Gift titles: "Gift " + 16-char random string * - Gift amounts: Random 1-99 value * - Contact names: Predefined test values * - DIDs: Uses test accounts (e.g., did:ethr:0x000...) * * Key Selectors: * - Contact list: 'li[data-testid="contactListItem"]' * - Gift recording: '#sectionRecordSomethingGiven' * - Contact name: '[data-testid="contactName"] input' * - Alert dialogs: 'div[role="alert"]' * * Timeouts & Retries: * - Uses OS-specific timeouts (longer for Linux) * - Implements retry logic for network operations * - Waits for UI animations and state changes * * Alert Handling: * - Closes onboarding dialogs * - Handles registration prompts * - Verifies alert dismissal * * State Requirements: * - Clean database state * - No existing contacts for test DIDs * - Available API rate limits * * @example Basic contact addition * ```typescript * await page.goto('./contacts'); * await page.getByPlaceholder('URL or DID, Name, Public Key') * .fill('did:ethr:0x000...., User Name'); * await page.locator('button > svg.fa-plus').click(); * ``` */ import { test, expect } from '@playwright/test'; import { importUser } from './testUtils'; test('Create new project, then search for it', async ({ page }) => { // Generate a random string of 16 characters let randomString = Math.random().toString(36).substring(2, 18); // In case the string is shorter than 16 characters, generate more characters until it is 16 characters long while (randomString.length < 16) { randomString += Math.random().toString(36).substring(2, 18); } const finalRandomString = randomString.substring(0, 16); // Standard texts const standardTitle = 'Idea '; const standardDescription = 'Description of Idea '; const standardEdit = ' EDITED'; const standardWebsite = 'https://example.com'; const editedWebsite = 'https://example.com/edited'; // Set dates const today = new Date(); const oneMonthAhead = new Date(today.setDate(today.getDate() + 30)); const twoMonthsAhead = new Date(today.setDate(today.getDate() + 30)); const finalDate = oneMonthAhead.toISOString().split('T')[0]; const editedDate = twoMonthsAhead.toISOString().split('T')[0]; // Set times const now = new Date(); const oneHourAhead = new Date(now.setHours(now.getHours() + 1)); const twoHoursAhead = new Date(now.setHours(now.getHours() + 1)); const finalHour = oneHourAhead.getHours().toString().padStart(2, '0'); const editedHour = twoHoursAhead.getHours().toString().padStart(2, '0'); const finalMinute = oneHourAhead.getMinutes().toString().padStart(2, '0'); const finalTime = `${finalHour}:${finalMinute}`; const editedTime = `${editedHour}:${finalMinute}`; // Combine texts with the random string const finalTitle = standardTitle + finalRandomString; const finalDescription = standardDescription + finalRandomString; const editedTitle = finalTitle + standardEdit; const editedDescription = finalDescription + standardEdit; // Import user 00 await importUser(page, '00'); // Create new project await page.goto('./projects'); // close onboarding, but not with a click to go to the main screen await page.locator('div > svg.fa-xmark').click(); await page.locator('button > svg.fa-plus').click(); await page.getByPlaceholder('Idea Name').fill(finalTitle); await page.getByPlaceholder('Description').fill(finalDescription); await page.getByPlaceholder('Website').fill(standardWebsite); await page.getByPlaceholder('Start Date').fill(finalDate); await page.getByPlaceholder('Start Time').fill(finalTime); await page.getByRole('button', { name: 'Save Project' }).click(); // Check texts await expect(page.locator('h2')).toContainText(finalTitle); await expect(page.locator('#Content')).toContainText(finalDescription); // Search for newly-created project in /projects await page.goto('./projects'); await expect(page.locator('ul#listProjects li').filter({ hasText: finalTitle })).toBeVisible(); // Search for newly-created project in /discover await page.goto('./discover'); await page.getByPlaceholder('Search…').fill(finalRandomString); await page.locator('#QuickSearch button').click(); await expect(page.locator('ul#listDiscoverResults li').filter({ hasText: finalTitle })).toBeVisible(); // Edit the project await page.locator('a').filter({ hasText: finalTitle }).first().click(); await page.getByRole('button', { name: 'Edit' }).click(); await expect(page.getByPlaceholder('Idea Name')).toHaveValue(finalTitle); // Check that textfield value has loaded before proceeding await page.getByPlaceholder('Idea Name').fill(editedTitle); await page.getByPlaceholder('Description').fill(editedDescription); await page.getByPlaceholder('Website').fill(editedWebsite); await page.getByPlaceholder('Start Date').fill(editedDate); await page.getByPlaceholder('Start Time').fill(editedTime); await page.getByRole('button', { name: 'Save Project' }).click(); // Check edits await expect(page.locator('h2')).toContainText(editedTitle); await page.getByText('Read More').click(); await expect(page.locator('#Content')).toContainText(editedDescription); });