forked from jsnbuchanan/crowd-funder-for-time-pwa
- Add better error handling and logging for gift recording flow - Add explicit navigation to contacts page before finding gift button - Add info icon click handling when needed - Add more comprehensive button detection with multiple selectors - Add debug logging for page state and navigation - Add screenshot capture on failures - Add retry logic with proper state verification - Fix linter errors in playwright config The changes help diagnose and handle various UI states that can occur during gift recording, making the tests more reliable especially on Linux.
543 lines
22 KiB
TypeScript
543 lines
22 KiB
TypeScript
/**
|
|
* Contact Management and Gift Recording Test Suite
|
|
*
|
|
* This test suite verifies the contact management and gift recording functionality
|
|
* of the application. It includes tests for adding contacts, recording gifts,
|
|
* and confirming gifts.
|
|
*
|
|
* Key Components:
|
|
*
|
|
* 1. Constants
|
|
* - ALERT_TIMEOUT: For alert-related operations (5000ms)
|
|
* - NETWORK_TIMEOUT: For network operations (10000ms)
|
|
* - ANIMATION_TIMEOUT: For animation completion (1000ms)
|
|
*
|
|
* 2. Main Test Cases
|
|
* - "Add contact, record gift, confirm gift"
|
|
* Tests complete flow of adding contact and managing gifts
|
|
* - "Without being registered, add contacts without registration"
|
|
* Verifies contact addition without registration
|
|
* - "Add contact, copy details, delete, and import"
|
|
* Tests contact import/export functionality
|
|
*
|
|
* 3. Helper Functions
|
|
* - generateRandomString: Creates unique test identifiers
|
|
* - dismissAlertWithRetry: Handles alert dismissal with retry logic
|
|
* - recordGift: Encapsulates gift recording workflow
|
|
* - confirmGift: Manages gift confirmation process
|
|
*
|
|
* Best Practices:
|
|
* - Comprehensive error handling with try-catch blocks
|
|
* - Random test data generation
|
|
* - Consistent verification steps
|
|
* - Page object patterns for maintainability
|
|
* - Debug logging support
|
|
* - Cross-browser compatibility considerations
|
|
*
|
|
* @file 40-add-contact.spec.ts
|
|
*/
|
|
|
|
import { test, expect, Page } from '@playwright/test';
|
|
import { importUser, getOSSpecificTimeout } from './testUtils';
|
|
|
|
// Update timeout constants for Linux
|
|
const BASE_TIMEOUT = getOSSpecificTimeout();
|
|
const ALERT_TIMEOUT = BASE_TIMEOUT / 6;
|
|
const NETWORK_TIMEOUT = BASE_TIMEOUT / 3;
|
|
const ANIMATION_TIMEOUT = 1000;
|
|
|
|
// Add test configuration to increase timeout
|
|
test.describe('Contact Management', () => {
|
|
// Increase timeout for all tests in this group
|
|
test.setTimeout(BASE_TIMEOUT * 2);
|
|
|
|
test('Add contact, record gift, confirm gift', async ({ page }) => {
|
|
try {
|
|
// Generate test data with error checking
|
|
const randomString = await generateRandomString(16);
|
|
const randomNonZeroNumber = Math.floor(Math.random() * 99) + 1;
|
|
if (randomNonZeroNumber <= 0) throw new Error('Failed to generate valid number');
|
|
|
|
const finalTitle = `Gift ${randomString}`;
|
|
const contactName = 'Contact #000 renamed';
|
|
const userName = 'User #000';
|
|
|
|
// Import user with error handling
|
|
try {
|
|
await importUser(page, '01');
|
|
} catch (e) {
|
|
throw new Error(`Failed to import user: ${e instanceof Error ? e.message : String(e)}`);
|
|
}
|
|
|
|
// Add new contact with verification
|
|
await page.goto('./contacts');
|
|
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(`did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F, ${userName}`);
|
|
await page.locator('button > svg.fa-plus').click();
|
|
|
|
// Handle the registration alert properly
|
|
await handleRegistrationAlert(page);
|
|
|
|
// Add a small delay to ensure UI is stable
|
|
await page.waitForTimeout(500);
|
|
|
|
// Verify contact was added and is clickable
|
|
const contactElement = page.locator('li.border-b');
|
|
await expect(contactElement).toContainText(userName, { timeout: ANIMATION_TIMEOUT });
|
|
|
|
// Ensure no alerts are present before clicking
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
// Click the info icon with force option if needed
|
|
await page.locator(`li[data-testid="contactListItem"] h2:has-text("${userName}") + span svg.fa-circle-info`).click({ force: true });
|
|
|
|
// Wait for navigation to contact details page
|
|
await expect(page.getByRole('heading', { name: 'Identifier Details' })).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
|
|
|
// Click edit button and wait for navigation
|
|
await page.locator('h2 svg.fa-pen').click();
|
|
|
|
// Debug: Log all headings on the page
|
|
const headings = await page.locator('h1, h2, h3, h4, h5, h6').allInnerTexts();
|
|
console.log('Available headings:', headings);
|
|
|
|
// Then look for the actual heading we expect to see
|
|
await expect(page.getByRole('heading', { name: 'Contact Methods' })).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
|
|
|
// Now look for the input field
|
|
const nameInput = page.getByTestId('contactName').locator('input');
|
|
await expect(nameInput).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
|
await expect(nameInput).toHaveValue(userName);
|
|
|
|
// Perform rename with verification
|
|
await nameInput.fill(contactName);
|
|
await page.getByRole('button', { name: 'Save' }).click();
|
|
|
|
// Wait for save to complete and verify new name
|
|
await expect(page.locator('h2', { hasText: contactName })).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
|
|
|
// Record gift with error handling
|
|
try {
|
|
await recordGift(page, contactName, finalTitle, randomNonZeroNumber);
|
|
} catch (e) {
|
|
throw new Error(`Failed to record gift: ${e instanceof Error ? e.message : String(e)}`);
|
|
}
|
|
|
|
// Switch users with verification
|
|
try {
|
|
await switchToUser00(page);
|
|
} catch (e) {
|
|
throw new Error(`Failed to switch users: ${e instanceof Error ? e.message : String(e)}`);
|
|
}
|
|
|
|
// Confirm gift with error handling
|
|
await confirmGift(page, finalTitle);
|
|
|
|
} catch (error) {
|
|
// Add more context to the error
|
|
if (error instanceof Error && error.message.includes('Edit Contact')) {
|
|
console.error('Failed to find Edit page heading. Available elements:', await page.locator('*').allInnerTexts());
|
|
}
|
|
throw error;
|
|
}
|
|
});
|
|
});
|
|
|
|
// Helper functions
|
|
async function generateRandomString(length: number): Promise<string> {
|
|
let result = Math.random().toString(36).substring(2, 18);
|
|
while (result.length < length) {
|
|
result += Math.random().toString(36).substring(2, 18);
|
|
}
|
|
return result.substring(0, length);
|
|
}
|
|
|
|
async function dismissAlertWithRetry(page: Page, maxRetries = 3) {
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click();
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden({ timeout: ANIMATION_TIMEOUT });
|
|
return;
|
|
} catch (e) {
|
|
if (i === maxRetries - 1) throw e;
|
|
await page.waitForTimeout(1000); // Wait before retry
|
|
}
|
|
}
|
|
}
|
|
|
|
async function recordGift(page: Page, contactName: string, title: string, amount: number) {
|
|
const TIMEOUT = getOSSpecificTimeout();
|
|
let retryCount = 3;
|
|
|
|
while (retryCount > 0) {
|
|
try {
|
|
console.log(`Gift recording attempt ${4 - retryCount}/3`);
|
|
|
|
// First navigate to home and ensure it's loaded
|
|
await page.goto('./', { timeout: TIMEOUT });
|
|
await Promise.all([
|
|
page.waitForLoadState('networkidle', { timeout: TIMEOUT }),
|
|
page.waitForLoadState('domcontentloaded', { timeout: TIMEOUT })
|
|
]);
|
|
|
|
// Handle onboarding first
|
|
const onboardingButton = page.getByTestId('closeOnboardingAndFinish');
|
|
if (await onboardingButton.isVisible()) {
|
|
console.log('Closing onboarding dialog...');
|
|
await onboardingButton.click();
|
|
await expect(onboardingButton).toBeHidden();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// Navigate to contact's details page
|
|
await page.goto('./contacts', { timeout: TIMEOUT });
|
|
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
|
|
|
// Debug current state
|
|
console.log('Current URL before clicking contact:', await page.url());
|
|
console.log('Looking for contact:', contactName);
|
|
|
|
// Find and click contact name
|
|
const contactHeading = page.getByRole('heading', { name: contactName }).first();
|
|
await expect(contactHeading).toBeVisible({ timeout: TIMEOUT });
|
|
await contactHeading.click();
|
|
|
|
// Wait for navigation
|
|
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
|
console.log('Current URL after clicking contact:', await page.url());
|
|
|
|
// Look for gift recording UI elements
|
|
const giftButton = page.locator([
|
|
'button:has-text("Record Gift")',
|
|
'button:has-text("Give")',
|
|
'[data-testid="recordGiftButton"]',
|
|
'a:has-text("Record Gift")',
|
|
'a:has-text("Give")'
|
|
].join(','));
|
|
|
|
// Debug UI state
|
|
const allButtons = await page.locator('button, a').allInnerTexts();
|
|
console.log('Available buttons:', allButtons);
|
|
|
|
// Check if we need to click info first
|
|
const infoIcon = page.locator('svg.fa-circle-info').first();
|
|
if (await infoIcon.isVisible()) {
|
|
console.log('Found info icon, clicking it first');
|
|
await infoIcon.click();
|
|
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
|
}
|
|
|
|
// Now look for gift button again
|
|
if (await giftButton.count() === 0) {
|
|
console.log('Gift button not found, taking screenshot');
|
|
await page.screenshot({ path: 'test-results/missing-gift-button.png', fullPage: true });
|
|
console.log('Page content:', await page.content());
|
|
throw new Error('Gift button not found on page');
|
|
}
|
|
|
|
await expect(giftButton).toBeVisible({ timeout: TIMEOUT });
|
|
await expect(giftButton).toBeEnabled({ timeout: TIMEOUT });
|
|
await giftButton.click();
|
|
|
|
// Wait for navigation and form
|
|
await Promise.all([
|
|
page.waitForLoadState('networkidle', { timeout: TIMEOUT }),
|
|
page.waitForLoadState('domcontentloaded', { timeout: TIMEOUT })
|
|
]);
|
|
|
|
const giftInput = page.getByPlaceholder('What was given');
|
|
await expect(giftInput).toBeVisible({ timeout: TIMEOUT });
|
|
|
|
// Fill form with verification between steps
|
|
await giftInput.fill(title);
|
|
await page.waitForTimeout(500);
|
|
|
|
const amountInput = page.getByRole('spinbutton');
|
|
await expect(amountInput).toBeVisible({ timeout: TIMEOUT });
|
|
await amountInput.fill(amount.toString());
|
|
await page.waitForTimeout(500);
|
|
|
|
// Submit and wait for response
|
|
const submitButton = page.getByRole('button', { name: 'Sign & Send' });
|
|
await expect(submitButton).toBeEnabled({ timeout: TIMEOUT });
|
|
await submitButton.click();
|
|
|
|
// Wait for confirmation with API check
|
|
const confirmationTimeout = Date.now() + TIMEOUT;
|
|
while (Date.now() < confirmationTimeout) {
|
|
const isVisible = await page.getByText('That gift was recorded.').isVisible();
|
|
if (isVisible) break;
|
|
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
await expect(page.getByText('That gift was recorded.')).toBeVisible({ timeout: 1000 });
|
|
|
|
// If we get here, everything worked
|
|
console.log('Gift recording successful');
|
|
return;
|
|
|
|
} catch (error) {
|
|
retryCount--;
|
|
console.log(`Gift recording attempt failed, ${retryCount} retries remaining`);
|
|
console.error('Error:', error instanceof Error ? error.message : String(error));
|
|
|
|
// Take screenshot on failure
|
|
if (!page.isClosed()) {
|
|
await page.screenshot({
|
|
path: `test-results/gift-recording-failure-${4 - retryCount}.png`,
|
|
fullPage: true
|
|
});
|
|
}
|
|
|
|
if (retryCount === 0) {
|
|
console.error('All gift recording attempts failed');
|
|
throw error;
|
|
}
|
|
|
|
await page.waitForTimeout(5000);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function switchToUser00(page: Page) {
|
|
await page.goto('./account');
|
|
await page.getByRole('heading', { name: 'Advanced' }).click();
|
|
await page.getByRole('link', { name: 'Switch Identifier' }).click();
|
|
await page.getByRole('link', { name: 'Add Another Identity…' }).click();
|
|
await page.getByText('You have a seed').click();
|
|
|
|
const 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';
|
|
await page.getByPlaceholder('Seed Phrase').fill(seedPhrase);
|
|
await page.getByRole('button', { name: 'Import' }).click();
|
|
|
|
await expect(page.getByRole('code')).toContainText('did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F',
|
|
{ timeout: NETWORK_TIMEOUT });
|
|
}
|
|
|
|
async function confirmGift(page: Page, title: string) {
|
|
const TIMEOUT = getOSSpecificTimeout();
|
|
|
|
try {
|
|
await page.goto('./', { timeout: TIMEOUT });
|
|
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
|
|
|
// Close onboarding if present
|
|
const onboardingButton = page.getByTestId('closeOnboardingAndFinish');
|
|
if (await onboardingButton.isVisible()) {
|
|
await onboardingButton.click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// Debug: Log page content
|
|
console.log('Page content before finding gift:', await page.content());
|
|
|
|
// Wait for and find the gift element
|
|
const giftElement = page.locator('li, div').filter({ hasText: title }).first();
|
|
await expect(giftElement).toBeVisible({ timeout: TIMEOUT });
|
|
console.log('Found gift element');
|
|
|
|
// Click and wait for navigation
|
|
await giftElement.click();
|
|
await Promise.all([
|
|
page.waitForLoadState('networkidle', { timeout: TIMEOUT }),
|
|
page.waitForLoadState('domcontentloaded', { timeout: TIMEOUT })
|
|
]);
|
|
|
|
// Debug: Log available elements
|
|
console.log('Page content after navigation:', await page.content());
|
|
|
|
// Try multiple selectors for confirm button
|
|
const confirmElement = page.locator([
|
|
'[data-testid="confirmGiftLink"]',
|
|
'[data-testid="confirmGiftButton"]',
|
|
'button:has-text("Confirm")',
|
|
'a:has-text("Confirm")'
|
|
].join(','));
|
|
|
|
await expect(confirmElement).toBeVisible({ timeout: TIMEOUT });
|
|
await confirmElement.click();
|
|
|
|
// Wait for confirmation
|
|
await expect(page.getByText('Confirmation submitted.')).toBeVisible({ timeout: TIMEOUT });
|
|
} catch (error) {
|
|
console.error('Confirmation failed:', error);
|
|
await page.screenshot({ path: 'test-results/confirmation-failure.png' });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function handleRegistrationAlert(page: Page) {
|
|
// Wait for the registration alert
|
|
await expect(page.locator('div[role="alert"]')).toBeVisible({ timeout: ALERT_TIMEOUT });
|
|
|
|
// Click "No" on registration prompt
|
|
await page.locator('div[role="alert"] button:has-text("No")').click();
|
|
|
|
// Wait for info alert and dismiss it
|
|
await dismissAlertWithRetry(page);
|
|
|
|
// Ensure all alerts are gone before proceeding
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden({ timeout: ANIMATION_TIMEOUT });
|
|
}
|
|
|
|
test('Without being registered, add contacts without registration', async ({ page, context }) => {
|
|
await page.goto('./account');
|
|
// wait until the DID shows on the page in the 'did' element
|
|
const didElem = await page.getByTestId('didWrapper').locator('code');
|
|
const newDid = await didElem.innerText();
|
|
expect(newDid.trim()).toEqual('');
|
|
|
|
// Add new contact without registering
|
|
await page.goto('./contacts');
|
|
await page.getByPlaceholder('URL or DID, Name, Public Key').fill('did:ethr:0x111d15564f824D56C7a07b913aA7aDd03382aA39, User #111');
|
|
await page.locator('button > svg.fa-plus').click();
|
|
await expect(page.locator('div[role="alert"] span:has-text("Contact Added")')).toBeVisible();
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
|
// wait for the alert to disappear, which also ensures that there is no "Register" button waiting
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
});
|
|
|
|
test('Add contact, copy details, delete, and import from paste & from file', async ({ page, context }) => {
|
|
await importUser(page, '00');
|
|
|
|
// Add new contact
|
|
await page.goto('./contacts');
|
|
await page.getByPlaceholder('URL or DID, Name, Public Key').fill('did:ethr:0x111d15564f824D56C7a07b913aA7aDd03382aA39, User #111');
|
|
await page.locator('button > svg.fa-plus').click();
|
|
await expect(page.locator('div[role="alert"]')).toBeVisible();
|
|
await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
|
// wait for the alert to disappear
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
// Add another new contact
|
|
await page.getByPlaceholder('URL or DID, Name, Public Key').fill('did:ethr:0x222BB77E6Ff3774d34c751f3c1260866357B677b, User #222, asdf1234');
|
|
await page.locator('button > svg.fa-plus').click();
|
|
await expect(page.locator('div[role="alert"]')).toBeVisible();
|
|
await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
await expect(page.getByTestId('contactListItem')).toHaveCount(2);
|
|
|
|
//// Copy contact details, export them, remove them, and paste to add them
|
|
|
|
// Copy contact details
|
|
await page.getByTestId('contactCheckAllTop').click();
|
|
await page.getByTestId('copySelectedContactsButtonTop').click();
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
// I would prefer to copy from the clipboard, but the recommended approaches don't work.
|
|
// See a different clipboard solution below.
|
|
|
|
// see contact details on the second contact
|
|
await page.getByTestId('contactListItem').nth(1).locator('a').click();
|
|
await page.getByRole('heading', { name: 'Identifier Details' }).isVisible();
|
|
// remove contact
|
|
await page.locator('button > svg.fa-trash-can').click();
|
|
await page.locator('div[role="alert"] button:has-text("Yes")').click();
|
|
// for some reason, .isHidden() (without expect) doesn't work
|
|
await expect(page.locator('div[role="alert"] button:has-text("Yes")')).toBeHidden();
|
|
|
|
// Firefox has a problem when we run this against the test server. It doesn't load the feed.
|
|
// It says there's a CORS problem; maybe it's more strict than the other browsers.
|
|
// It works when we set the config to use a local server.
|
|
// Seems like we hit a similar problem above.
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
// go to the contacts page and paste the copied contact details
|
|
await page.goto('./contacts');
|
|
// check that there are fewer contacts
|
|
await expect(page.getByTestId('contactListItem')).toHaveCount(1);
|
|
|
|
const contactData = 'Paste this: [{ "did": "did:ethr:0x111d15564f824D56C7a07b913aA7aDd03382aA39", "name": "User #111" }, { "did": "did:ethr:0x222BB77E6Ff3774d34c751f3c1260866357B677b", "name": "User #222", "publicKeyBase64": "asdf1234"}] '
|
|
await page.getByPlaceholder('URL or DID, Name, Public Key').fill(contactData);
|
|
await page.locator('button > svg.fa-plus').click();
|
|
// we're on the contact-import page
|
|
await expect(page.locator('li', { hasText: 'New' })).toHaveCount(1);
|
|
await expect(page.locator('span').filter({ hasText: 'the same as' })).toBeVisible();
|
|
await page.locator('button', { hasText: 'Import' }).click();
|
|
// check that there are more contacts
|
|
await expect(page.getByTestId('contactListItem')).toHaveCount(2);
|
|
|
|
// Import via the file backup-import, with both new and existing contacts
|
|
await page.goto('./account');
|
|
await page.getByRole('heading', { name: 'Advanced' }).click();
|
|
const fileSelect = await page.locator('input[type="file"]')
|
|
fileSelect.setInputFiles('./test-playwright/exported-data.json');
|
|
await page.locator('button', { hasText: 'Import Only Contacts' }).click();
|
|
// we're on the contact-import page
|
|
await expect(page.locator('li', { hasText: '- New' })).toHaveCount(3);
|
|
await expect(page.locator('li', { hasText: '- Existing' })).toHaveCount(1);
|
|
await expect(page.locator('span').filter({ hasText: 'the same as' })).toBeHidden();
|
|
await page.locator('button', { hasText: 'Import' }).click();
|
|
// check that there are more contacts
|
|
await expect(page.getByTestId('contactListItem')).toHaveCount(5);
|
|
// The visibility error is because currently the server returns an error for the same person.
|
|
// But it should only show that one, for User #000.
|
|
|
|
});
|
|
|
|
test('Copy contact to clipboard, then import ', async ({ page, context }, testInfo) => {
|
|
await importUser(page, '00');
|
|
|
|
await page.goto('./account');
|
|
await page.getByRole('heading', { name: 'Advanced' }).click();
|
|
const fileSelect = await page.locator('input[type="file"]')
|
|
fileSelect.setInputFiles('./test-playwright/exported-data.json');
|
|
await page.locator('button', { hasText: 'Import Only Contacts' }).click();
|
|
// we're on the contact-import page
|
|
await expect(page.getByRole('heading', { name: "Contact Import" })).toBeVisible();
|
|
await page.locator('button', { hasText: 'Import' }).click();
|
|
|
|
await page.goto('./contacts');
|
|
// Copy contact details
|
|
await page.getByTestId('contactCheckAllTop').click();
|
|
|
|
// // There's a crazy amount of overlap in all the userAgent values. Ug.
|
|
// const agent = await page.evaluate(() => {
|
|
// return navigator.userAgent;
|
|
// });
|
|
// console.log("agent: ", agent);
|
|
|
|
const isFirefox = await page.evaluate(() => {
|
|
return navigator.userAgent.includes('Firefox');
|
|
});
|
|
if (isFirefox) {
|
|
// Firefox doesn't grant permissions like this but it works anyway.
|
|
} else {
|
|
await context.grantPermissions(['clipboard-read']);
|
|
}
|
|
|
|
const isWebkit = await page.evaluate(() => {
|
|
return navigator.userAgent.includes('Macintosh') || navigator.userAgent.includes('iPhone');
|
|
});
|
|
if (isWebkit) {
|
|
console.log("Haven't found a way to access clipboard text in Webkit. Skipping.");
|
|
return;
|
|
}
|
|
|
|
console.log("Running test that copies contact details to clipboard.");
|
|
await page.getByTestId('copySelectedContactsButtonTop').click();
|
|
const clipboardText = await page.evaluate(async () => {
|
|
return navigator.clipboard.readText();
|
|
});
|
|
|
|
// look into the playwright.config file for the server URL
|
|
const webServer = testInfo.config.webServer;
|
|
const clientServerUrl = webServer?.url;
|
|
|
|
const PATH_PART = clientServerUrl + "/contact-import/";
|
|
expect(clipboardText).toContain(PATH_PART);
|
|
|
|
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert
|
|
await expect(page.locator('div[role="alert"]')).toBeHidden();
|
|
|
|
await page.goto(clipboardText);
|
|
// we're on the contact-import page
|
|
await expect(page.getByRole('heading', { name: "Contact Import" })).toBeVisible();
|
|
await expect(page.locator('span', { hasText: '4 contacts are the same' })).toBeVisible();
|
|
});
|