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
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);
|
|
});
|