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.
162 lines
7.8 KiB
162 lines
7.8 KiB
/**
|
|
* 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);
|
|
});
|
|
|