import { test, expect, Page } from '@playwright/test'; import { importUser, importUserFromAccount } from './testUtils'; import { createPerformanceCollector, attachPerformanceData, assertPerformanceMetrics } from './performanceUtils'; test('Record an offer', async ({ page }, testInfo) => { test.setTimeout(60000); // 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, 5); const description = `Offering of ${randomString}`; const updatedDescription = `Updated ${description}`; const randomNonZeroNumber = Math.floor(Math.random() * 998) + 1; // STEP 3: Import user and navigate to discover page await perfCollector.measureUserAction('import-user-account', async () => { await importUserFromAccount(page, "00"); }); await perfCollector.measureUserAction('navigate-to-discover', async () => { await page.goto('./discover'); }); const initialMetrics = await perfCollector.collectNavigationMetrics('discover-page-load'); await testInfo.attach('initial-page-load-metrics', { contentType: 'application/json', body: JSON.stringify(initialMetrics, null, 2) }); // STEP 4: Close onboarding and select project await perfCollector.measureUserAction('close-onboarding', async () => { await page.getByTestId('closeOnboardingAndFinish').click(); }); await perfCollector.measureUserAction('select-project', async () => { await page.locator('ul#listDiscoverResults li:nth-child(1)').click(); }); // STEP 5: Record an offer await perfCollector.measureUserAction('wait-for-edit-button', async () => { await page.locator('button', { hasText: 'Edit' }).isVisible(); }); await perfCollector.measureUserAction('click-offer-button', async () => { await page.getByTestId('offerButton').click(); }); await perfCollector.measureUserAction('fill-offer-details', async () => { await page.getByTestId('inputDescription').fill(description); await page.getByTestId('inputOfferAmount').fill(randomNonZeroNumber.toString()); }); await perfCollector.measureUserAction('submit-offer', async () => { expect(page.getByRole('button', { name: 'Sign & Send' })); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); }); // STEP 6: Navigate to projects and check offer await perfCollector.measureUserAction('navigate-to-projects', async () => { await page.goto('./projects'); }); await perfCollector.measureUserAction('click-offers-tab', async () => { await page.getByRole('link', { name: 'Offers', exact: true }).click(); }); await perfCollector.measureUserAction('click-offer-details', async () => { await page.locator('li').filter({ hasText: description }).locator('a').first().click(); }); await perfCollector.measureUserAction('verify-offer-details', async () => { await expect(page.getByRole('heading', { name: 'Verifiable Claim Details' })).toBeVisible(); await expect(page.getByText(description, { exact: true })).toBeVisible(); await expect(page.getByText('Offered to a bigger plan')).toBeVisible(); }); // STEP 7: Expand details and check public server const serverPagePromise = 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 serverPage = await serverPagePromise; await perfCollector.measureUserAction('verify-public-server', async () => { await expect(serverPage.getByText(description)).toBeVisible(); await expect(serverPage.getByText('did:none:HIDDEN')).toBeVisible(); }); // STEP 8: Update the offer await perfCollector.measureUserAction('navigate-back-to-projects', async () => { await page.goto('./projects'); }); await perfCollector.measureUserAction('click-offers-tab-again', async () => { await page.getByRole('link', { name: 'Offers', exact: true }).click(); }); await perfCollector.measureUserAction('click-offer-to-edit', async () => { await page.locator('li').filter({ hasText: description }).locator('a').first().click(); }); await perfCollector.measureUserAction('click-edit-button', async () => { await page.getByTestId('editClaimButton').click(); }); await perfCollector.measureUserAction('verify-edit-form', async () => { await page.locator('heading', { hasText: 'What is offered' }).isVisible(); const itemDesc = await page.getByTestId('itemDescription'); await expect(itemDesc).toHaveValue(description); const amount = await page.getByTestId('inputOfferAmount'); await expect(amount).toHaveValue(randomNonZeroNumber.toString()); }); await perfCollector.measureUserAction('update-offer-values', async () => { const itemDesc = await page.getByTestId('itemDescription'); await itemDesc.fill(updatedDescription); const amount = await page.getByTestId('inputOfferAmount'); await amount.fill(String(randomNonZeroNumber + 1)); }); await perfCollector.measureUserAction('submit-updated-offer', async () => { await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); }); // STEP 9: Verify updated offer await perfCollector.measureUserAction('navigate-to-projects-final', async () => { await page.goto('./projects'); }); await perfCollector.measureUserAction('click-offers-tab-final', async () => { await page.getByRole('link', { name: 'Offers', exact: true }).click(); }); await perfCollector.measureUserAction('click-updated-offer', async () => { await page.locator('li').filter({ hasText: description }).locator('a').first().click(); }); await perfCollector.measureUserAction('verify-updated-offer', async () => { const newItemDesc = page.getByTestId('description'); await expect(newItemDesc).toHaveText(updatedDescription); }); await perfCollector.measureUserAction('click-edit-button-final', async () => { await page.getByTestId('editClaimButton').click(); }); await perfCollector.measureUserAction('verify-updated-amount', async () => { const newAmount = page.getByTestId('inputOfferAmount'); await expect(newAmount).toHaveValue((randomNonZeroNumber + 1).toString()); }); // STEP 10: Check home page for new offers await perfCollector.measureUserAction('navigate-to-home', async () => { await page.goto('./'); }); await perfCollector.measureUserAction('verify-new-offers-indicator', async () => { const offerNumElem = page.getByTestId('newOffersToUserProjectsActivityNumber'); const offerNumText = await offerNumElem.textContent(); if (offerNumText === null) { throw new Error('Expected Activity Number greater than 0 but got null.'); } else if (offerNumText === '50+') { // we're OK } else if (parseInt(offerNumText) > 0) { // we're OK } else { throw new Error(`Expected Activity Number of greater than 0 but got ${offerNumText}.`); } }); await perfCollector.measureUserAction('click-new-offers-number', async () => { const offerNumElem = page.getByTestId('newOffersToUserProjectsActivityNumber'); await offerNumElem.click(); }); await perfCollector.measureUserAction('verify-new-offers-page', async () => { await expect(page.getByText('New Offers To Your Projects', { exact: true })).toBeVisible(); }); await perfCollector.measureUserAction('expand-offers-section', async () => { await page.getByTestId('showOffersToUserProjects').locator('div > svg.fa-chevron-right').click(); }); await perfCollector.measureUserAction('verify-offer-in-list', async () => { await expect(page.getByText(description)).toBeVisible(); }); // 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); }); test('Affirm delivery of an offer', async ({ page }, testInfo) => { // STEP 1: Initialize the performance collector const perfCollector = await createPerformanceCollector(page); // STEP 2: Import user and navigate to home await perfCollector.measureUserAction('import-user-account', async () => { await importUserFromAccount(page, "00"); }); await perfCollector.measureUserAction('navigate-to-home', 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) }); await perfCollector.measureUserAction('close-onboarding', async () => { await page.getByTestId('closeOnboardingAndFinish').click(); }); // STEP 3: Check new offers indicator await perfCollector.measureUserAction('verify-new-offers-indicator', async () => { const offerNumElem = page.getByTestId('newOffersToUserProjectsActivityNumber'); await expect(offerNumElem).toBeVisible(); }); // STEP 4: Navigate to offers list await perfCollector.measureUserAction('click-new-offers-number', async () => { // Close any dialog overlays that might be blocking clicks await page.waitForTimeout(1000); 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); const offerNumElem = page.getByTestId('newOffersToUserProjectsActivityNumber'); await offerNumElem.click(); }); await perfCollector.measureUserAction('click-offers-link', async () => { await page.getByTestId('showOffersToUserProjects').locator('a').click(); }); // STEP 5: Affirm delivery await perfCollector.measureUserAction('select-first-offer', async () => { const firstItem = page.getByTestId('listRecentOffersToUserProjects').locator('li').first(); await expect(firstItem).toBeVisible(); await firstItem.locator('svg.fa-file-lines').click(); }); await perfCollector.measureUserAction('verify-claim-details', async () => { await expect(page.getByText('Verifiable Claim Details', { exact: true })).toBeVisible(); }); await perfCollector.measureUserAction('click-affirm-delivery', async () => { await page.getByRole('button', { name: 'Affirm Delivery' }).click(); }); await perfCollector.measureUserAction('fill-delivery-details', async () => { await page.getByPlaceholder('What was given').fill('Whatever the offer says'); await page.getByRole('spinbutton').fill('2'); }); await perfCollector.measureUserAction('submit-delivery', 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 6: 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); });