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.
 
 
 
 
 
 

277 lines
10 KiB

import { expect, Page } from '@playwright/test';
// Import the seed and switch to the user based on the ID.
// '01' -> user 111
// otherwise -> user 000
// (... which is a weird convention but I haven't taken the time to change it)
export async function importUser(page: Page, id?: string): Promise<string> {
let seedPhrase, userName, did;
// Set seed phrase and DID based on user ID
switch(id) {
case '01':
seedPhrase = 'island fever beef wine urban aim vacant quit afford total poem flame service calm better adult neither color gaze forum month sister imitate excite';
userName = 'User One';
did = 'did:ethr:0x111d15564f824D56C7a07b913aA7aDd03382aA39';
break;
default: // to user 00
seedPhrase = 'rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage';
userName = 'User Zero';
did = 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F';
}
// Import ID
await page.goto('./start');
await page.getByText('You have a seed').click();
await page.getByPlaceholder('Seed Phrase').fill(seedPhrase);
await page.getByRole('button', { name: 'Import' }).click();
// Check DID
await expect(page.getByRole('code')).toContainText(did);
// ... and ensure the app retrieves the registration status
await expect(page.locator('#sectionUsageLimits').getByText('Checking')).toBeHidden();
return did;
}
export async function importUserAndCloseOnboarding(page: Page, id?: string): Promise<string> {
const did = await importUser(page, id);
await page.goto('./');
await page.getByTestId('closeOnboardingAndFinish').click();
return did;
}
// This is to switch to someone already in the identity table. It doesn't include registration.
export async function switchToUser(page: Page, did: string): Promise<void> {
// This is the direct approach but users have to tap on things so we'll do that instead.
//await page.goto('./identity-switcher');
await page.goto('./account');
await page.getByRole('heading', { name: 'Advanced' }).click();
await page.getByRole('link', { name: 'Switch Identifier' }).click();
const didElem = await page.locator(`code:has-text("${did}")`);
await didElem.isVisible();
await didElem.click();
// wait for the switch to happen and the account page to fully load
await page.getByTestId('didWrapper').locator('code:has-text("did:")');
}
function createContactName(did: string): string {
return "User " + did.slice(11, 14);
}
export async function deleteContact(page: Page, did: string, contactName: string) {
// Navigate to contacts page
await page.goto('./contacts');
// Wait for page to load completely
await page.waitForLoadState('networkidle');
// Check if we need to hide the "Show Actions" view first
const loadingCount = await page.locator('.loading-indicator').count();
if (loadingCount > 0) {
await page.locator('.loading-indicator').first().waitFor({ state: 'hidden' });
}
// Check if "Hide Actions" button exists (meaning we're in the give numbers view)
const showGiveNumbersExists = await page.getByRole('button', { name: 'Hide Actions' }).count();
if (showGiveNumbersExists > 0) {
await page.getByRole('button', { name: 'Hide Actions' }).click();
}
// Look for the contact by name
const contactItems = page.locator('li[data-testid="contactListItem"]');
const contactCount = await contactItems.count();
// Debug: Print all contact names if no match found
if (contactCount === 0) {
await page.screenshot({ path: 'debug-no-contacts.png' });
throw new Error(`No contacts found on page. Screenshot saved as debug-no-contacts.png`);
}
// Check if our contact exists
const contactExists = await contactItems.filter({ hasText: contactName }).count();
if (contactExists === 0) {
// Try alternative selectors
const selectors = [
'li',
'div[data-testid="contactListItem"]',
'.contact-item',
'[data-testid*="contact"]'
];
for (const selector of selectors) {
const testCount = await page.locator(selector).filter({ hasText: contactName }).count();
if (testCount > 0) {
// Found working selector, use it
const contactItem = page.locator(selector).filter({ hasText: contactName }).first();
// Look for info icon or delete button
const infoIconExists = await contactItem.locator('svg.fa-info-circle').count();
if (infoIconExists > 0) {
await contactItem.locator('svg.fa-info-circle').click();
await page.waitForLoadState('networkidle');
// Should now be on the contact detail page
await expect(page.getByText('Contact Details')).toBeVisible();
// Look for delete button
const deleteButtonExists = await page.getByRole('button', { name: 'Delete Contact' }).count();
if (deleteButtonExists > 0) {
await page.getByRole('button', { name: 'Delete Contact' }).click();
// Handle confirmation dialog
await expect(page.getByRole('button', { name: 'Yes, Delete' })).toBeVisible();
await page.getByRole('button', { name: 'Yes, Delete' }).click();
// Wait for dialog to close
await expect(page.getByRole('button', { name: 'Yes, Delete' })).toBeHidden();
return;
}
}
}
}
throw new Error(`Contact "${contactName}" not found on contacts page`);
}
// Use the standard flow
const contactItem = contactItems.filter({ hasText: contactName }).first();
// Look for info icon
const infoIconExists = await contactItem.locator('svg.fa-info-circle').count();
if (infoIconExists > 0) {
await contactItem.locator('svg.fa-info-circle').click();
await page.waitForLoadState('networkidle');
// Should now be on the contact detail page
await expect(page.getByText('Contact Details')).toBeVisible();
// Look for delete button
const deleteButtonExists = await page.getByRole('button', { name: 'Delete Contact' }).count();
if (deleteButtonExists > 0) {
await page.getByRole('button', { name: 'Delete Contact' }).click();
// Handle confirmation dialog
await expect(page.getByRole('button', { name: 'Yes, Delete' })).toBeVisible();
await page.getByRole('button', { name: 'Yes, Delete' }).click();
// Wait for dialog to close
await expect(page.getByRole('button', { name: 'Yes, Delete' })).toBeHidden();
}
}
}
export async function generateNewEthrUser(page: Page): Promise<string> {
await page.goto('./start');
await page.getByTestId('newSeed').click();
await expect(page.locator('span:has-text("Created")')).toBeVisible();
await page.goto('./account');
const didElem = await page.getByTestId('didWrapper').locator('code:has-text("did:")');
const newDid = await didElem.innerText();
return newDid;
}
// Generate a new random user and register them.
// Note that this makes 000 the active user. Use switchToUser to switch to this DID.
export async function generateAndRegisterEthrUser(page: Page): Promise<string> {
const newDid = await generateNewEthrUser(page);
await importUser(page, '000'); // switch to user 000
await page.goto('./contacts');
const contactName = createContactName(newDid);
const contactInput = `${newDid}, ${contactName}`;
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(contactInput);
await page.locator('button > svg.fa-plus').click();
// Wait for the contact to be added first
await expect(page.locator('li', { hasText: contactName })).toBeVisible();
// Wait longer for the registration alert to appear (it has a 1-second timeout)
await page.waitForTimeout(2000);
// Check if the registration alert is present
const alertCount = await page.locator('div[role="alert"]').count();
if (alertCount > 0) {
// Check if this is a registration alert (contains "Yes" button)
const yesButtonCount = await page.locator('div[role="alert"] button:has-text("Yes")').count();
if (yesButtonCount > 0) {
// register them
await page.locator('div[role="alert"] button:has-text("Yes")').click();
// wait for it to disappear because the next steps may depend on alerts being gone
await expect(page.locator('div[role="alert"] button:has-text("Yes")')).toBeHidden();
}
}
return newDid;
}
// Function to generate a random string of specified length
export async function generateRandomString(length: number): Promise<string> {
return Math.random().toString(36).substring(2, 2 + length);
}
// Function to create an array of unique strings
export async function createUniqueStringsArray(count: number): Promise<string[]> {
const stringsArray: string[] = [];
const stringLength = 16;
for (let i = 0; i < count; i++) {
let randomString = await generateRandomString(stringLength);
stringsArray.push(randomString);
}
return stringsArray;
}
// Function to create an array of two-digit non-zero numbers
export async function createRandomNumbersArray(count: number): Promise<number[]> {
const numbersArray: number[] = [];
for (let i = 0; i < count; i++) {
let randomNumber = Math.floor(Math.random() * 99) + 1;
numbersArray.push(randomNumber);
}
return numbersArray;
}
export function isLinuxEnvironment() {
return process.platform === 'linux';
}
export function getOSSpecificTimeout(): number {
// Increase base timeout for Linux
const isLinux = process.platform === 'linux';
return isLinux ? 180000 : 60000; // 3 minutes for Linux, 1 minute for others
}
export function getOSSpecificConfig() {
if (isLinuxEnvironment()) {
return {
retries: 2,
timeout: 90000, // Increased global timeout
expect: {
timeout: 30000 // Increased expect timeout
},
// Add video recording for failed tests on Linux
use: {
video: 'retain-on-failure',
trace: 'retain-on-failure'
}
};
}
return {};
}
// Add helper for test grouping
export function isResourceIntensiveTest(testPath: string): boolean {
return testPath.includes('35-record-gift-from-image-share') ||
testPath.includes('40-add-contact');
}