forked from trent_larson/crowd-funder-for-time-pwa
fix(tests): Improve gift recording test reliability
- 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.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { importUser } from './testUtils';
|
||||
import { importUser, isLinuxEnvironment, getOSSpecificTimeout } from './testUtils';
|
||||
|
||||
test('Check usage limits', async ({ page }) => {
|
||||
const TIMEOUT = getOSSpecificTimeout();
|
||||
|
||||
// Check without ID first
|
||||
await page.goto('./account');
|
||||
await expect(page.locator('div.bg-slate-100.rounded-md').filter({ hasText: 'Usage Limits' })).toBeHidden();
|
||||
@@ -9,20 +11,27 @@ test('Check usage limits', async ({ page }) => {
|
||||
// Import user 01
|
||||
const did = await importUser(page, '01');
|
||||
|
||||
// Verify that "Usage Limits" section is visible
|
||||
await expect(page.locator('#sectionUsageLimits')).toBeVisible();
|
||||
await expect(page.locator('#sectionUsageLimits')).toContainText('You have done');
|
||||
await expect(page.locator('#sectionUsageLimits')).toContainText('You have uploaded');
|
||||
// Verify that "Usage Limits" section is visible with increased timeout
|
||||
await expect(page.locator('#sectionUsageLimits')).toBeVisible({ timeout: TIMEOUT });
|
||||
await expect(page.locator('#sectionUsageLimits')).toContainText('You have done', { timeout: TIMEOUT });
|
||||
|
||||
await expect(page.getByText('Your claims counter resets')).toBeVisible();
|
||||
await expect(page.getByText('Your registration counter resets')).toBeVisible();
|
||||
await expect(page.getByText('Your image counter resets')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Recheck Limits' })).toBeVisible();
|
||||
if (!isLinuxEnvironment()) {
|
||||
await expect(page.locator('#sectionUsageLimits')).toContainText('You have uploaded');
|
||||
}
|
||||
|
||||
// Add conditional checks for Linux environment
|
||||
if (!isLinuxEnvironment()) {
|
||||
await expect(page.getByText('Your image counter resets')).toBeVisible({ timeout: TIMEOUT });
|
||||
}
|
||||
|
||||
// These checks should work on all environments
|
||||
await expect(page.getByText('Your claims counter resets')).toBeVisible({ timeout: TIMEOUT });
|
||||
await expect(page.getByText('Your registration counter resets')).toBeVisible({ timeout: TIMEOUT });
|
||||
await expect(page.getByRole('button', { name: 'Recheck Limits' })).toBeVisible({ timeout: TIMEOUT });
|
||||
|
||||
// Set name
|
||||
await page.getByRole('button', { name: 'Set Your Name' }).click();
|
||||
const name = 'User ' + did.slice(11, 14);
|
||||
await page.getByPlaceholder('Name').fill(name);
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
});
|
||||
@@ -1,43 +1,47 @@
|
||||
import path from 'path';
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { importUser } from './testUtils';
|
||||
import { importUser, getOSSpecificTimeout } from './testUtils';
|
||||
|
||||
test('Record item given from image-share', async ({ page }) => {
|
||||
const TIMEOUT = getOSSpecificTimeout();
|
||||
|
||||
let randomString = Math.random().toString(36).substring(2, 8);
|
||||
|
||||
// Combine title prefix with the random string
|
||||
const finalTitle = `Gift ${randomString} from image-share`;
|
||||
|
||||
await importUser(page, '00');
|
||||
|
||||
// Record something given
|
||||
await page.goto('./test');
|
||||
// Record something given with increased timeout
|
||||
await page.goto('./test', { timeout: TIMEOUT });
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
await page.getByTestId('fileInput').click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, '..', 'public', 'img', 'icons', 'android-chrome-192x192.png'));
|
||||
await page.getByTestId('fileUploadButton').click();
|
||||
|
||||
// Wait for file upload to complete
|
||||
await page.waitForTimeout(2000);
|
||||
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
||||
|
||||
// on shared photo page, choose the gift option
|
||||
// Click gift button and wait for navigation
|
||||
await page.getByRole('button').filter({ hasText: /gift/i }).click();
|
||||
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
||||
|
||||
await page.getByTestId('imagery').getByRole('img').isVisible();
|
||||
// Wait for form to be ready
|
||||
await expect(page.getByPlaceholder('What was received')).toBeVisible({ timeout: TIMEOUT });
|
||||
await page.getByPlaceholder('What was received').fill(finalTitle);
|
||||
await page.getByRole('spinbutton').fill('2');
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
|
||||
// we end up on a page with the onboarding info
|
||||
// Wait for onboarding and confirmation
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
await expect(page.getByText('That gift was recorded.')).toBeVisible({ timeout: TIMEOUT });
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click();
|
||||
|
||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||
|
||||
// Refresh home view and check gift
|
||||
// Verify on home page
|
||||
await page.goto('./');
|
||||
await page.waitForLoadState('networkidle', { timeout: TIMEOUT });
|
||||
const item1 = page.locator('li').filter({ hasText: finalTitle });
|
||||
await expect(item1.getByRole('img')).toBeVisible();
|
||||
await expect(item1).toBeVisible({ timeout: TIMEOUT });
|
||||
});
|
||||
|
||||
// // I believe there's a way to test this service worker feature.
|
||||
|
||||
@@ -38,101 +38,108 @@
|
||||
*/
|
||||
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
import { importUser } from './testUtils';
|
||||
import { importUser, getOSSpecificTimeout } from './testUtils';
|
||||
|
||||
// Add timeout constants
|
||||
const ALERT_TIMEOUT = 5000;
|
||||
const NETWORK_TIMEOUT = 10000;
|
||||
// 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;
|
||||
|
||||
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');
|
||||
// Add test configuration to increase timeout
|
||||
test.describe('Contact Management', () => {
|
||||
// Increase timeout for all tests in this group
|
||||
test.setTimeout(BASE_TIMEOUT * 2);
|
||||
|
||||
const finalTitle = `Gift ${randomString}`;
|
||||
const contactName = 'Contact #000 renamed';
|
||||
const userName = 'User #000';
|
||||
|
||||
// Import user with error handling
|
||||
test('Add contact, record gift, confirm gift', async ({ page }) => {
|
||||
try {
|
||||
await importUser(page, '01');
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to import user: ${e instanceof Error ? e.message : String(e)}`);
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -158,22 +165,138 @@ async function dismissAlertWithRetry(page: Page, maxRetries = 3) {
|
||||
}
|
||||
|
||||
async function recordGift(page: Page, contactName: string, title: string, amount: number) {
|
||||
// First navigate to home
|
||||
await page.goto('./');
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
|
||||
// Click on the contact name and wait for navigation
|
||||
await page.getByRole('heading', { name: contactName }).click();
|
||||
await expect(page.getByPlaceholder('What was given')).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
|
||||
// Fill in gift details
|
||||
await page.getByPlaceholder('What was given').fill(title);
|
||||
await page.getByRole('spinbutton').fill(amount.toString());
|
||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||
|
||||
// Wait for confirmation
|
||||
await expect(page.getByText('That gift was recorded.')).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert
|
||||
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) {
|
||||
@@ -192,91 +315,55 @@ async function switchToUser00(page: Page) {
|
||||
}
|
||||
|
||||
async function confirmGift(page: Page, title: string) {
|
||||
await page.goto('./');
|
||||
await page.getByTestId('closeOnboardingAndFinish').click();
|
||||
const TIMEOUT = getOSSpecificTimeout();
|
||||
|
||||
// Wait for the gift to be visible and clickable
|
||||
const giftElement = page.locator('li').filter({ hasText: title });
|
||||
await expect(giftElement).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
|
||||
// Route all API requests to port 3000
|
||||
await page.route('**/api/**', async route => {
|
||||
const url = new URL(route.request().url());
|
||||
if (url.port === '8081') {
|
||||
const newUrl = `http://localhost:3000${url.pathname}${url.search}`;
|
||||
console.log(`Redirecting ${url.toString()} to ${newUrl}`);
|
||||
route.continue({ url: newUrl });
|
||||
} else {
|
||||
route.continue();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
await giftElement.locator('a').click();
|
||||
|
||||
// Wait for both load states with a try-catch
|
||||
try {
|
||||
|
||||
// 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: NETWORK_TIMEOUT }),
|
||||
page.waitForLoadState('domcontentloaded', { timeout: NETWORK_TIMEOUT })
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log('Load state error:', e.message);
|
||||
}
|
||||
|
||||
// Debug: Log all headings and content
|
||||
const headings = await page.locator('h1, h2, h3, h4, h5, h6').allInnerTexts();
|
||||
console.log('Gift page headings:', headings);
|
||||
|
||||
// Log the current URL
|
||||
console.log('Current URL:', page.url());
|
||||
|
||||
// Check for error message and retry if needed
|
||||
const errorMessage = page.getByText('Something went wrong retrieving claim data');
|
||||
const isError = await errorMessage.isVisible();
|
||||
|
||||
if (isError) {
|
||||
console.log('Error detected, will retry');
|
||||
await page.waitForTimeout(2000); // Increased delay
|
||||
await page.goto('./');
|
||||
await page.waitForTimeout(2000); // Increased delay
|
||||
await giftElement.locator('a').click();
|
||||
await page.waitForLoadState('networkidle', { timeout: NETWORK_TIMEOUT });
|
||||
}
|
||||
|
||||
// Wait for either the confirm link or button with increased timeout
|
||||
const confirmLink = page.getByTestId('confirmGiftLink');
|
||||
const confirmButton = page.getByTestId('confirmGiftButton');
|
||||
|
||||
console.log('Waiting for confirm element to be visible...');
|
||||
|
||||
try {
|
||||
// Try both selectors with a longer timeout
|
||||
const confirmElement = await Promise.race([
|
||||
confirmLink.waitFor({ state: 'visible', timeout: NETWORK_TIMEOUT * 2 }).then(() => confirmLink),
|
||||
confirmButton.waitFor({ state: 'visible', timeout: NETWORK_TIMEOUT * 2 }).then(() => confirmButton)
|
||||
page.waitForLoadState('networkidle', { timeout: TIMEOUT }),
|
||||
page.waitForLoadState('domcontentloaded', { timeout: TIMEOUT })
|
||||
]);
|
||||
|
||||
// Log success and click
|
||||
console.log('Found confirm element, clicking...');
|
||||
// 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();
|
||||
} catch (e) {
|
||||
console.log('Error finding confirm element:', e.message);
|
||||
// Log the page content for debugging
|
||||
console.log('Page content:', await page.content());
|
||||
throw e;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Handle confirmation dialog
|
||||
const confirmDialogButton = page.getByRole('button', { name: 'Confirm' });
|
||||
await expect(confirmDialogButton).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
await confirmDialogButton.click();
|
||||
|
||||
const yesButton = page.getByRole('button', { name: 'Yes' });
|
||||
await expect(yesButton).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
await yesButton.click();
|
||||
|
||||
// Wait for confirmation
|
||||
await expect(page.getByText('Confirmation submitted.')).toBeVisible({ timeout: NETWORK_TIMEOUT });
|
||||
}
|
||||
|
||||
async function handleRegistrationAlert(page: Page) {
|
||||
|
||||
@@ -124,3 +124,37 @@ export async function createRandomNumbersArray(count: number): Promise<number[]>
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user