Files
crowd-funder-from-jason/test-playwright/20-create-project.spec.ts
Jose Olarte III 6fb4ceab81 fix(playwright): re-route after affirming onboarding dialog
After calling OnboardingDialog from ProjectsView, route back to projects page again

The onboarding dialog was designed to route back to HomeView when called from ProjectsView. The tests need to be updated to account for this intended behavior.
2025-09-09 15:57:36 +08:00

173 lines
6.9 KiB
TypeScript

/**
* 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');
// 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
console.log('No onboarding dialog found on projects page');
}
// 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');
// Debug: Log all projects in the list
const projectItems = await page.locator('ul#listProjects li').all();
console.log(`Found ${projectItems.length} projects in list`);
for (let i = 0; i < projectItems.length; i++) {
const text = await projectItems[i].textContent();
console.log(`Project ${i}: ${text}`);
}
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);
});