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.
 
 
 
 
 
 

492 lines
18 KiB

/**
* @file Gift Recording Test Suite
* @description Tests TimeSafari's core gift recording functionality with integrated performance tracking
*
* This test covers a complete gift recording flow in TimeSafari with integrated performance tracking.
*
* Focus areas:
* - Performance monitoring for every major user step
* - Gift creation, recording, and verification
* - Public server integration and validation
* - Validation of both behavior and responsiveness
*
* @version 1.0.0
* @author Matthew Raymer
* @lastModified 2025-08-02
*
* ================================================================================
* TEST OVERVIEW
* ================================================================================
*
* This test verifies the complete gift recording workflow from data generation to
* public verification, ensuring end-to-end functionality works correctly with
* comprehensive performance monitoring.
*
* Core Test Objectives:
* 1. Gift Creation & Recording
* - Random gift title generation with uniqueness
* - Random non-zero amount assignment (1-99 range)
* - Proper form filling and validation
* - JWT signing and submission with performance tracking
*
* 2. Gift Verification & Display
* - Gift appears in home view after recording
* - Details match input data exactly
* - Verifiable claim details are accessible
* - UI elements display correctly
*
* 3. Public Verification & Integration
* - Gift viewable on public endorser server
* - Claim details properly exposed via API
* - Cross-platform compatibility (Chromium/Firefox)
*
* ================================================================================
* TEST FLOW & PROCESS
* ================================================================================
*
* Phase 1: Data Generation & Preparation
* ────────────────────────────────────────────────────────────────────────────────
* 1. Generate unique test data:
* - Random 4-character string for gift ID uniqueness
* - Random amount between 1-99 (non-zero validation)
* - Combine with "Gift " prefix for standard format
*
* 2. User preparation:
* - Import User 00 (test account with known state)
* - Navigate to home page
* - Handle onboarding dialog closure
*
* Phase 2: Gift Recording Process
* ────────────────────────────────────────────────────────────────────────────────
* 3. Recipient selection:
* - Click "Person" button to open recipient picker
* - Select "Unnamed/Unknown" recipient
* - Verify selection is applied
*
* 4. Gift details entry:
* - Fill gift title with generated unique string
* - Enter random amount in number field
* - Validate form state before submission
*
* 5. Submission and signing:
* - Click "Sign & Send" button
* - Wait for JWT signing process
* - Verify success notification appears
* - Dismiss any info alerts
*
* Phase 3: Verification & Validation
* ────────────────────────────────────────────────────────────────────────────────
* 6. Home view verification:
* - Refresh home page to load new gift
* - Locate gift in activity list by title
* - Click info link to view details
*
* 7. Details verification:
* - Verify "Verifiable Claim Details" heading
* - Confirm gift title matches exactly
* - Expand Details section for extended info
*
* 8. Public server integration:
* - Click "View on Public Server" link
* - Verify popup opens with correct URL
* - Validate public server accessibility
*
* ================================================================================
* TEST DATA SPECIFICATIONS
* ================================================================================
*
* Gift Title Format: "Gift [4-char-random]"
* - Prefix: "Gift " (with space)
* - Random component: 4-character alphanumeric string
* - Example: "Gift a7b3", "Gift x9y2"
*
* Amount Range: 1-99 (inclusive)
* - Minimum: 1 (non-zero validation)
* - Maximum: 99 (reasonable upper bound)
* - Type: Integer only
* - Example: 42, 7, 99
*
* Recipient: "Unnamed/Unknown"
* - Standard test recipient
* - No specific DID or contact info
* - Used for all test gifts
*
* ================================================================================
* SELECTOR REFERENCE
* ================================================================================
*
* Form Elements:
* - Gift title input: '[data-testid="giftTitle"]' or 'input[placeholder="What was given"]'
* - Amount input: 'input[type="number"]' or 'input[role="spinbutton"]'
* - Submit button: 'button[name="Sign & Send"]'
* - Person button: 'button[name="Person"]'
* - Recipient list: 'ul[role="listbox"]'
*
* Navigation & UI:
* - Onboarding close: '[data-testid="closeOnboardingAndFinish"]'
* - Home page: './' (relative URL)
* - Alert dismissal: 'div[role="alert"] button > svg.fa-xmark'
* - Success message: 'text="That gift was recorded."'
*
* Verification Elements:
* - Gift list item: 'li:first-child' (filtered by title)
* - Info link: '[data-testid="circle-info-link"]'
* - Details heading: 'h2[name="Verifiable Claim Details"]'
* - Details section: 'h2[name="Details", exact="true"]'
* - Public server link: 'a[name="View on the Public Server"]'
*
* ================================================================================
* ERROR HANDLING & DEBUGGING
* ================================================================================
*
* Common Failure Points:
* 1. Onboarding Dialog
* - Issue: Dialog doesn't close properly
* - Debug: Check if closeOnboardingAndFinish button exists
* - Fix: Add wait for dialog to be visible before clicking
*
* 2. Recipient Selection
* - Issue: "Unnamed" recipient not found
* - Debug: Check if recipient list is populated
* - Fix: Add wait for list to load before filtering
*
* 3. Form Submission
* - Issue: "Sign & Send" button not clickable
* - Debug: Check if form is valid and all fields filled
* - Fix: Add validation before submission
*
* 4. Success Verification
* - Issue: Success message doesn't appear
* - Debug: Check network requests and JWT signing
* - Fix: Add longer timeout for signing process
*
* 5. Home View Refresh
* - Issue: Gift doesn't appear in list
* - Debug: Check if gift was actually recorded
* - Fix: Add wait for home view to reload
*
* 6. Public Server Integration
* - Issue: Popup doesn't open or wrong URL
* - Debug: Check if public server is accessible
* - Fix: Verify endorser server configuration
*
* Debugging Commands:
* ```bash
* # Run with trace for detailed debugging
* npx playwright test 30-record-gift.spec.ts --trace on
*
* # Run with headed browser for visual debugging
* npx playwright test 30-record-gift.spec.ts --headed
*
* # Run with slow motion for step-by-step debugging
* npx playwright test 30-record-gift.spec.ts --debug
* ```
*
* ================================================================================
* BROWSER COMPATIBILITY
* ================================================================================
*
* Tested Browsers:
* - Chromium: Primary target, full functionality
* - Firefox: Secondary target, may have timing differences
*
* Browser-Specific Considerations:
* - Firefox: May require longer timeouts for form interactions
* - Chromium: Generally faster, more reliable
* - Both: Popup handling may differ slightly
*
* ================================================================================
* PERFORMANCE CONSIDERATIONS
* ================================================================================
*
* Expected Timings:
* - Data generation: < 1ms
* - User import: 2-5 seconds
* - Form filling: 1-2 seconds
* - JWT signing: 3-8 seconds
* - Home refresh: 2-4 seconds
* - Public server: 1-3 seconds
*
* Total expected runtime: 10-20 seconds
*
* Performance Monitoring:
* - Monitor JWT signing time (most variable)
* - Track home view refresh time
* - Watch for memory leaks in popup handling
*
* ================================================================================
* MAINTENANCE GUIDELINES
* ================================================================================
*
* When Modifying This Test:
* 1. Update version number and lastModified date
* 2. Test on both Chromium and Firefox
* 3. Verify with different random data sets
* 4. Check that public server integration still works
* 5. Update selector references if UI changes
*
* Related Files to Monitor:
* - src/views/RecordGiftView.vue (gift recording UI)
* - src/views/HomeView.vue (gift display)
* - sw_scripts/safari-notifications.js (JWT signing)
* - src/libs/endorserServer.ts (API integration)
* - test-playwright/testUtils.ts (user management)
*
* ================================================================================
* INTEGRATION POINTS
* ================================================================================
*
* Dependencies:
* - User 00 must be available in test data
* - Endorser server must be running and accessible
* - Public server must be configured correctly
* - JWT signing must be functional
*
* API Endpoints Used:
* - POST /api/claims (gift recording)
* - GET /api/claims (public verification)
* - WebSocket connections for real-time updates
*
* ================================================================================
* SECURITY CONSIDERATIONS
* ================================================================================
*
* Test Data Security:
* - Random data prevents test interference
* - No sensitive information in test gifts
* - Public server verification is read-only
*
* JWT Handling:
* - Test uses test user credentials
* - Signing process is isolated
* - No production keys used
*
* ================================================================================
* RELATED DOCUMENTATION
* ================================================================================
*
* @see test-playwright/testUtils.ts - User management utilities
* @see test-playwright/README.md - General testing guidelines
* @see docs/user-guides/gift-recording.md - User workflow documentation
* @see src/views/RecordGiftView.vue - Implementation details
* @see sw_scripts/safari-notifications.js - JWT signing implementation
*
* @example Complete test execution
* ```bash
* # Run this specific test
* npx playwright test 30-record-gift.spec.ts
*
* # Run with detailed output
* npx playwright test 30-record-gift.spec.ts --reporter=list
*
* # Run in headed mode for debugging
* npx playwright test 30-record-gift.spec.ts --headed
* ```
*/
import { test, expect } from '@playwright/test';
import { importUserFromAccount } from './testUtils';
import {
createPerformanceCollector,
attachPerformanceData,
assertPerformanceMetrics
} from './performanceUtils';
/**
* @test Record something given
* @description End-to-end test of gift recording functionality with performance tracking
* @tags gift-recording, e2e, user-workflow, performance
* @timeout 45000ms (45 seconds for JWT signing and API calls)
*
* @process
* 1. Generate unique test data
* 2. Import test user and navigate to home
* 3. Record gift with random title and amount
* 4. Verify gift appears in home view
* 5. Check public server integration
*
* @data
* - Gift title: "Gift [random-4-chars]"
* - Amount: Random 1-99
* - Recipient: "Unnamed/Unknown"
*
* @verification
* - Success notification appears
* - Gift visible in home view
* - Details match input data
* - Public server accessible
*
* @browsers chromium, firefox
* @retries 2 (for flaky network conditions)
*/
test('Record something given', async ({ page }, testInfo) => {
// STEP 1: Initialize the performance collector
const perfCollector = await createPerformanceCollector(page);
// STEP 2: Generate unique test data
const randomString = Math.random().toString(36).substring(2, 6);
const randomNonZeroNumber = Math.floor(Math.random() * 99) + 1;
const standardTitle = 'Gift ';
const finalTitle = standardTitle + randomString;
// STEP 3: Import user 00 and navigate to home page
await perfCollector.measureUserAction('import-user-account', async () => {
await importUserFromAccount(page, '00');
});
await perfCollector.measureUserAction('initial-navigation', async () => {
await page.goto('./');
});
const initialMetrics = await perfCollector.collectNavigationMetrics('home-page-load');
await testInfo.attach('initial-page-load-metrics', {
contentType: 'application/json',
body: JSON.stringify(initialMetrics, null, 2)
});
// STEP 4: Close onboarding dialog
await perfCollector.measureUserAction('close-onboarding', async () => {
await page.getByTestId('closeOnboardingAndFinish').click();
});
// STEP 4.5: Close any additional dialogs that might be blocking
await perfCollector.measureUserAction('close-additional-dialogs', async () => {
// Wait a moment for any dialogs to appear
await page.waitForTimeout(1000);
// Try to close any remaining dialogs
const closeButtons = page.locator('button[aria-label*="close"], button[aria-label*="Close"], .dialog-overlay button, [role="dialog"] button');
const count = await closeButtons.count();
for (let i = 0; i < count; i++) {
try {
await closeButtons.nth(i).click({ timeout: 2000 });
} catch (e) {
// Ignore errors if button is not clickable
}
}
// Wait for any animations to complete
await page.waitForTimeout(500);
});
// STEP 5: Select recipient
await perfCollector.measureUserAction('select-recipient', async () => {
await page.getByRole('button', { name: 'Person' }).click();
await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click();
});
// STEP 6: Fill gift details
await perfCollector.measureUserAction('fill-gift-details', async () => {
await page.getByPlaceholder('What was given').fill(finalTitle);
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
});
// STEP 7: Submit gift and verify success
await perfCollector.measureUserAction('submit-gift', async () => {
await page.getByRole('button', { name: 'Sign & Send' }).click();
await expect(page.getByText('That gift was recorded.')).toBeVisible();
await page.locator('div[role="alert"] button > svg.fa-xmark').click();
});
// STEP 8: Refresh home view and locate gift
await perfCollector.measureUserAction('refresh-home-view', async () => {
// Try page.reload() instead of goto to see if that helps
await page.reload();
});
await perfCollector.collectNavigationMetrics('home-refresh-load');
// Wait for feed to load and gift to appear
await perfCollector.measureUserAction('wait-for-feed-load', async () => {
// Wait for the feed container to be present
await page.locator('ul').first().waitFor({ state: 'visible', timeout: 15000 });
// Wait for any feed items to load (not just the first one)
await page.locator('li').first().waitFor({ state: 'visible', timeout: 15000 });
// Debug: Check what's actually in the feed
const feedItems = page.locator('li');
const count = await feedItems.count();
// Try to find our gift in any position, not just first
let giftFound = false;
for (let i = 0; i < count; i++) {
try {
const itemText = await feedItems.nth(i).textContent();
if (itemText?.includes(finalTitle)) {
giftFound = true;
break;
}
} catch (e) {
// Continue to next item
}
}
if (!giftFound) {
// Wait a bit more and try again
await page.waitForTimeout(3000);
// Check again
const newCount = await feedItems.count();
for (let i = 0; i < newCount; i++) {
try {
const itemText = await feedItems.nth(i).textContent();
if (itemText?.includes(finalTitle)) {
giftFound = true;
break;
}
} catch (e) {
// Continue to next item
}
}
}
if (!giftFound) {
throw new Error(`Gift with title "${finalTitle}" not found in feed after waiting`);
}
});
// Find the gift item (could be in any position)
const item = page.locator('li').filter({ hasText: finalTitle });
// STEP 9: View gift details
await perfCollector.measureUserAction('view-gift-details', async () => {
// Debug: Check what elements are actually present
// Wait for the item to be visible
await item.waitFor({ state: 'visible', timeout: 10000 });
// Check if the circle-info-link exists
const circleInfoLink = item.locator('[data-testid="circle-info-link"]');
const isVisible = await circleInfoLink.isVisible();
// If not visible, let's see what's in the item
if (!isVisible) {
const itemHtml = await item.innerHTML();
}
await circleInfoLink.click();
});
await expect(page.getByRole('heading', { name: 'Verifiable Claim Details' })).toBeVisible();
await expect(page.getByText(finalTitle, { exact: true })).toBeVisible();
// STEP 10: Expand details and open public server
const page1Promise = page.waitForEvent('popup');
await perfCollector.measureUserAction('expand-details', async () => {
await page.getByRole('heading', { name: 'Details', exact: true }).click();
});
await perfCollector.measureUserAction('open-public-server', async () => {
await page.getByRole('link', { name: 'View on the Public Server' }).click();
});
const page1 = await page1Promise;
// STEP 11: Attach and validate performance data
const { webVitals, performanceReport, summary } = await attachPerformanceData(testInfo, perfCollector);
const avgNavigationTime = perfCollector.navigationMetrics.reduce((sum, nav) =>
sum + nav.metrics.loadComplete, 0) / perfCollector.navigationMetrics.length;
assertPerformanceMetrics(webVitals, initialMetrics, avgNavigationTime);
});