You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							167 lines
						
					
					
						
							6.6 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							167 lines
						
					
					
						
							6.6 KiB
						
					
					
				
								/**
							 | 
						|
								 * 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 +
							 | 
						|
								    " ... and enough text to overflow into the 'Read More' section.";
							 | 
						|
								
							 | 
						|
								  // Import user 00
							 | 
						|
								  await importUser(page, '00');
							 | 
						|
								
							 | 
						|
								  // Create new project
							 | 
						|
								  await page.goto('./projects');
							 | 
						|
								  // Check if onboarding dialog exists and close it if present
							 | 
						|
								  try {
							 | 
						|
								    await page.getByTestId('closeOnboardingAndFinish').click({ timeout: 2000 });
							 | 
						|
								    await page.waitForFunction(() => {
							 | 
						|
								      return !document.querySelector('.dialog-overlay');
							 | 
						|
								    }, { timeout: 5000 });
							 | 
						|
								  } catch (error) {
							 | 
						|
								    // No onboarding dialog present, continue
							 | 
						|
								  }
							 | 
						|
								  // Route back to projects page again, because the onboarding dialog was designed to route to HomeView when called from ProjectsView
							 | 
						|
								  await page.goto('./projects');
							 | 
						|
								  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();
							 | 
						|
								
							 | 
						|
								  // Wait for project to be saved and page to update
							 | 
						|
								  await page.waitForLoadState('networkidle');
							 | 
						|
								  
							 | 
						|
								  // 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');
							 | 
						|
								  // Wait for projects list to load and then search for the project
							 | 
						|
								  await page.waitForLoadState('networkidle');
							 | 
						|
								  
							 | 
						|
								  await expect(page.locator('ul#listProjects li').filter({ hasText: finalTitle })).toBeVisible({ timeout: 10000 });
							 | 
						|
								
							 | 
						|
								  // 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);
							 | 
						|
								});
							 |