/** * This test covers a complete user flow in TimeSafari with integrated performance tracking. * * Focus areas: * - Performance monitoring for every major user step * - Multi-user flow using DID switching * - Offer creation, viewing, and state updates * - Validation of both behavior and responsiveness */ import { test, expect } from '@playwright/test'; import { switchToUser, importUserFromAccount } from './testUtils'; import { createPerformanceCollector, attachPerformanceData, assertPerformanceMetrics } from './performanceUtils'; test('New offers for another user', async ({ page }, testInfo) => { // STEP 1: Initialize the performance collector const perfCollector = await createPerformanceCollector(page); // STEP 2: Navigate to home page and measure baseline performance 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 3: Extract the auto-created DID from the page // Wait for the page to be ready and the DID to be available await page.waitForSelector('#Content[data-active-did]', { timeout: 10000 }); const autoCreatedDid = await page.getAttribute('#Content', 'data-active-did'); if (!autoCreatedDid) throw new Error('Auto-created DID not found in HomeView'); // STEP 4: Close onboarding dialog and confirm no new offers are visible await perfCollector.measureUserAction('close-onboarding', async () => { await page.getByTestId('closeOnboardingAndFinish').click(); }); await expect(page.getByTestId('newDirectOffersActivityNumber')).toBeHidden(); // STEP 5: Switch to User Zero, who will create offers await perfCollector.measureUserAction('import-user-account', async () => { await importUserFromAccount(page, "00"); }); // STEP 6: Navigate to contacts page await perfCollector.measureUserAction('navigate-to-contacts', async () => { await page.goto('/contacts'); }); await perfCollector.collectNavigationMetrics('contacts-page-load'); // STEP 7: Add the auto-created DID as a contact await perfCollector.measureUserAction('add-contact', async () => { await page.getByPlaceholder('URL or DID, Name, Public Key').fill(autoCreatedDid + ', A Friend'); await page.locator('button > svg.fa-plus').click(); await page.locator('div[role="alert"] button:has-text("No")').click(); await expect(page.locator('div[role="alert"] span:has-text("Success")')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); }); // STEP 8: Show action buttons for making offers await perfCollector.measureUserAction('show-actions', async () => { await page.getByRole('button').filter({ hasText: /See Actions/i }).click(); }); // STEP 9 & 10: Create two offers for the auto-created user const randomString1 = Math.random().toString(36).substring(2, 5); await perfCollector.measureUserAction('create-first-offer', async () => { await page.getByTestId('offerButton').click(); await page.getByTestId('inputDescription').fill(`help of ${randomString1} from #000`); await page.getByTestId('inputOfferAmount').fill('1'); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"]').filter({ hasText: 'That offer was recorded.' }).locator('button > svg.fa-xmark').click(); // Wait for alert to be hidden to prevent multiple dialogs await expect(page.locator('div[role="alert"]').filter({ hasText: 'That offer was recorded.' })).toBeHidden(); }); // Add delay between offers to prevent performance issues await page.waitForTimeout(500); const randomString2 = Math.random().toString(36).substring(2, 5); await perfCollector.measureUserAction('create-second-offer', async () => { await page.getByTestId('offerButton').click(); await page.getByTestId('inputDescription').fill(`help of ${randomString2} from #000`); await page.getByTestId('inputOfferAmount').fill('3'); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"]').filter({ hasText: 'That offer was recorded.' }).locator('button > svg.fa-xmark').click(); // Wait for alert to be hidden to prevent multiple dialogs await expect(page.locator('div[role="alert"]').filter({ hasText: 'That offer was recorded.' })).toBeHidden(); }); // STEP 11: Switch back to the auto-created DID await perfCollector.measureUserAction('switch-user', async () => { await switchToUser(page, autoCreatedDid); }); // STEP 12: Navigate back home as the auto-created user await perfCollector.measureUserAction('navigate-home-as-other-user', async () => { await page.goto('/'); }); await perfCollector.collectNavigationMetrics('home-return-load'); // STEP 13: Confirm 2 new offers are visible let offerNumElem = page.getByTestId('newDirectOffersActivityNumber'); await expect(offerNumElem).toHaveText('2'); // STEP 14 & 15: View and expand the offers list await perfCollector.measureUserAction('view-offers-list', async () => { await offerNumElem.click(); }); await expect(page.getByText('New Offers To You', { exact: true })).toBeVisible(); await perfCollector.measureUserAction('expand-offers', async () => { await page.getByTestId('showOffersToUser').locator('div > svg.fa-chevron-right').click(); }); // STEP 16: Validate both offers are displayed await expect(page.getByText(`help of ${randomString2} from #000`)).toBeVisible(); await expect(page.getByText(`help of ${randomString1} from #000`)).toBeVisible(); // STEP 17: Mark one offer as read await perfCollector.measureUserAction('mark-offers-as-read', async () => { const liElem = page.locator('li').filter({ hasText: `help of ${randomString2} from #000` }); // Hover over the li element to make the "keep all above" text visible await liElem.hover(); await liElem.locator('div').filter({ hasText: /keep all above/ }).click(); }); // STEP 18 & 19: Return home and check that the count has dropped to 1 await perfCollector.measureUserAction('final-home-navigation', async () => { await page.goto('/'); }); await perfCollector.collectNavigationMetrics('final-home-load'); offerNumElem = page.getByTestId('newDirectOffersActivityNumber'); await expect(offerNumElem).toHaveText('1'); // STEP 20: Open the offers list again to confirm the remaining offer await perfCollector.measureUserAction('final-offer-check', async () => { await offerNumElem.click(); await expect(page.getByText('New Offer To You', { exact: true })).toBeVisible(); await page.getByTestId('showOffersToUser').locator('div > svg.fa-chevron-right').click(); }); // STEP 21 & 22: Final verification that the UI reflects the read/unread state correctly await perfCollector.measureUserAction('final-verification', async () => { await page.goto('/'); await page.locator('#listLatestActivity li').first().waitFor({ state: 'visible' }); }); await expect(page.getByTestId('newDirectOffersActivityNumber')).toBeHidden(); // STEP 23: 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); });