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.
 
 
 
 
 
 

299 lines
12 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): Promise<void> {
console.log('[DEBUG] deleteContact: Starting deletion for DID:', did);
await page.goto('./contacts');
console.log('[DEBUG] deleteContact: Navigated to contacts page');
// Wait for the page to load completely
await page.waitForLoadState('networkidle');
console.log('[DEBUG] deleteContact: Page load completed');
// Check if there are any loading indicators or filters active
const loadingIndicator = page.locator('[data-testid*="loading"], .loading, .spinner');
const loadingCount = await loadingIndicator.count();
console.log('[DEBUG] deleteContact: Loading indicators found:', loadingCount);
// Check for any filter or state buttons that might be active
const showGiveNumbers = page.locator('button:has-text("Hide Actions")');
const showGiveNumbersExists = await showGiveNumbers.count();
console.log('[DEBUG] deleteContact: "Hide Actions" button exists (showing give numbers):', showGiveNumbersExists > 0);
if (showGiveNumbersExists > 0) {
console.log('[DEBUG] deleteContact: Clicking "Hide Actions" to show normal view');
await showGiveNumbers.click();
await page.waitForTimeout(1000); // Wait for UI to update
}
const contactName = createContactName(did);
console.log('[DEBUG] deleteContact: Looking for contact with name:', contactName);
// First, let's see what contacts are actually on the page
const contactItems = await page.locator('li[data-testid="contactListItem"]');
const contactCount = await contactItems.count();
console.log('[DEBUG] deleteContact: Found contact items on page:', contactCount);
// Log all contact names visible on the page
for (let i = 0; i < contactCount; i++) {
const contactItem = contactItems.nth(i);
const nameElement = contactItem.locator('h2');
const nameText = await nameElement.textContent();
console.log('[DEBUG] deleteContact: Contact', i, ':', nameText);
}
// If no contacts found, let's take a screenshot to see what's on the page
if (contactCount === 0) {
console.log('[DEBUG] deleteContact: No contacts found, taking screenshot');
await page.screenshot({ path: 'debug-no-contacts.png', fullPage: true });
console.log('[DEBUG] deleteContact: Screenshot saved as debug-no-contacts.png');
}
// Try to find the contact list item with the expected name
const contactListItem = page.locator(`li[data-testid="contactListItem"]:has(h2:has-text("${contactName}"))`);
const contactExists = await contactListItem.count();
console.log('[DEBUG] deleteContact: Contact with name exists:', contactName, contactExists > 0);
if (contactExists === 0) {
console.error('[DEBUG] deleteContact: Contact not found on page:', contactName);
throw new Error(`Contact "${contactName}" not found on contacts page`);
}
// Now click the info icon - fix the selector to match actual DOM structure
// Try different selectors for the info icon
const infoIconSelectors = [
`li[data-testid="contactListItem"]:has(h2:has-text("${contactName}")) font-awesome[icon="circle-info"]`,
`li[data-testid="contactListItem"]:has(h2:has-text("${contactName}")) .fa-circle-info`,
`li[data-testid="contactListItem"]:has(h2:has-text("${contactName}")) svg.fa-circle-info`,
`li[data-testid="contactListItem"]:has(h2:has-text("${contactName}")) [class*="fa-circle-info"]`
];
let infoIcon: import('@playwright/test').Locator | null = null;
let infoIconExists = 0;
for (const selector of infoIconSelectors) {
console.log('[DEBUG] deleteContact: Trying selector:', selector);
const testIcon = page.locator(selector);
const testCount = await testIcon.count();
console.log('[DEBUG] deleteContact: Selector result count:', testCount);
if (testCount > 0) {
infoIcon = testIcon;
infoIconExists = testCount;
console.log('[DEBUG] deleteContact: Found working selector:', selector);
break;
}
}
console.log('[DEBUG] deleteContact: Info icon exists:', infoIconExists > 0);
if (infoIconExists === 0 || !infoIcon) {
console.error('[DEBUG] deleteContact: Info icon not found for contact:', contactName);
throw new Error(`Info icon not found for contact "${contactName}"`);
}
// go to the detail page for this contact
await infoIcon.click();
console.log('[DEBUG] deleteContact: Clicked info icon, should be on detail page');
// Verify we're on the detail page
const detailPageHeading = page.locator('h1:has-text("Identifier Details")');
await detailPageHeading.waitFor({ timeout: 10000 });
console.log('[DEBUG] deleteContact: Confirmed on detail page');
// delete the contact
const deleteButton = page.locator('button > svg.fa-trash-can');
const deleteButtonExists = await deleteButton.count();
console.log('[DEBUG] deleteContact: Delete button exists:', deleteButtonExists > 0);
if (deleteButtonExists === 0) {
console.error('[DEBUG] deleteContact: Delete button not found');
throw new Error('Delete button not found on detail page');
}
await deleteButton.click();
console.log('[DEBUG] deleteContact: Clicked delete button');
// Confirm deletion
const confirmButton = page.locator('div[role="alert"] button:has-text("Yes")');
await confirmButton.waitFor({ timeout: 10000 });
console.log('[DEBUG] deleteContact: Confirmation dialog appeared');
await confirmButton.click();
console.log('[DEBUG] deleteContact: Clicked confirmation button');
// for some reason, .isHidden() (without expect) doesn't work
await expect(page.locator('div[role="alert"] button:has-text("Yes")')).toBeHidden();
console.log('[DEBUG] deleteContact: Confirmation dialog dismissed, deletion complete');
}
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> {
console.log('[DEBUG] generateAndRegisterEthrUser: Starting user generation');
const newDid = await generateNewEthrUser(page);
console.log('[DEBUG] generateAndRegisterEthrUser: Generated new DID:', newDid);
await importUser(page, '000'); // switch to user 000
console.log('[DEBUG] generateAndRegisterEthrUser: Switched to user 000');
await page.goto('./contacts');
console.log('[DEBUG] generateAndRegisterEthrUser: Navigated to contacts page');
const contactName = createContactName(newDid);
console.log('[DEBUG] generateAndRegisterEthrUser: Created contact name:', contactName);
const contactInput = `${newDid}, ${contactName}`;
console.log('[DEBUG] generateAndRegisterEthrUser: Filling contact input with:', contactInput);
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(contactInput);
await page.locator('button > svg.fa-plus').click();
console.log('[DEBUG] generateAndRegisterEthrUser: Clicked add contact button');
// register them
await page.locator('div[role="alert"] button:has-text("Yes")').click();
console.log('[DEBUG] generateAndRegisterEthrUser: Clicked registration confirmation');
// 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();
console.log('[DEBUG] generateAndRegisterEthrUser: Registration dialog dismissed');
await expect(page.locator('li', { hasText: contactName })).toBeVisible();
console.log('[DEBUG] generateAndRegisterEthrUser: Contact is now visible in list:', contactName);
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');
}