177 lines
7.0 KiB
TypeScript
177 lines
7.0 KiB
TypeScript
|
|
import { test, expect } from '@playwright/test';
|
||
|
|
import { TEST_CREDENTIALS } from './helpers/auth';
|
||
|
|
import { epic, feature, severity, description } from './helpers/allure';
|
||
|
|
|
||
|
|
test.describe('Authentication Tests', () => {
|
||
|
|
test.setTimeout(90000);
|
||
|
|
test.beforeEach(async ({ page }) => {
|
||
|
|
epic('User Authentication');
|
||
|
|
feature('Login');
|
||
|
|
|
||
|
|
// Retry the initial navigation in place - on a loaded local machine a
|
||
|
|
// single headless navigation can occasionally stall well past one
|
||
|
|
// timeout even though the server itself responds in ~1-3s.
|
||
|
|
let lastError: unknown;
|
||
|
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
||
|
|
try {
|
||
|
|
await page.goto(TEST_CREDENTIALS.loginPath, { waitUntil: 'domcontentloaded' });
|
||
|
|
lastError = undefined;
|
||
|
|
break;
|
||
|
|
} catch (error) {
|
||
|
|
lastError = error;
|
||
|
|
console.log(`Login page navigation attempt ${attempt} failed, retrying...`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (lastError) throw lastError;
|
||
|
|
|
||
|
|
await page.waitForTimeout(2000);
|
||
|
|
|
||
|
|
// Switch to English by clicking the article element
|
||
|
|
console.log('Checking for language switcher...');
|
||
|
|
await page.waitForTimeout(1000);
|
||
|
|
|
||
|
|
const languageArticle = page.locator('article.ant-typography.font-poppins-500.css-1kfsfla').first();
|
||
|
|
const isVisible = await languageArticle.isVisible({ timeout: 2000 }).catch(() => false);
|
||
|
|
|
||
|
|
if (isVisible) {
|
||
|
|
const text = await languageArticle.textContent();
|
||
|
|
console.log(`Found language button with text: "${text}"`);
|
||
|
|
console.log('Clicking to switch language...');
|
||
|
|
await languageArticle.click();
|
||
|
|
await page.waitForTimeout(2000);
|
||
|
|
console.log('✓ Language switched');
|
||
|
|
} else {
|
||
|
|
console.log('Language button not found');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should successfully login with valid credentials', async ({ page }) => {
|
||
|
|
severity('critical');
|
||
|
|
description('Test successful login flow with valid user credentials');
|
||
|
|
|
||
|
|
console.log('Testing login with valid credentials...');
|
||
|
|
|
||
|
|
await test.step('Fill in username', async () => {
|
||
|
|
await page.fill('input[type="email"], input[name="email"], input[placeholder*="email" i]', TEST_CREDENTIALS.username);
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Fill in password', async () => {
|
||
|
|
await page.fill('input[type="password"], input[name="password"]', TEST_CREDENTIALS.password);
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Click login button', async () => {
|
||
|
|
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Verify redirect to dashboard', async () => {
|
||
|
|
await page.waitForURL('**/dashboard**', { timeout: 30000 }).catch(() => {
|
||
|
|
console.log('Checking for successful navigation...');
|
||
|
|
});
|
||
|
|
await page.waitForLoadState('domcontentloaded').catch(() => {});
|
||
|
|
await page.waitForTimeout(2000);
|
||
|
|
|
||
|
|
const currentUrl = page.url();
|
||
|
|
console.log('Current URL after login:', currentUrl);
|
||
|
|
expect(currentUrl).toContain('dashboard');
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Login successful');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should show error with invalid credentials', async ({ page }) => {
|
||
|
|
severity('critical');
|
||
|
|
description('Verify error handling for invalid login credentials');
|
||
|
|
|
||
|
|
console.log('Testing login with invalid credentials...');
|
||
|
|
|
||
|
|
await test.step('Fill in invalid username', async () => {
|
||
|
|
await page.fill('input[type="email"], input[name="email"], input[placeholder*="email" i]', 'invalid@example.com');
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Fill in invalid password', async () => {
|
||
|
|
await page.fill('input[type="password"], input[name="password"]', 'wrongpassword');
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Click login button', async () => {
|
||
|
|
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Verify error message appears', async () => {
|
||
|
|
await page.waitForTimeout(2000);
|
||
|
|
|
||
|
|
const errorMessage = await page.locator('text=/error|invalid|incorrect|failed/i').first().isVisible().catch(() => false);
|
||
|
|
const stillOnLoginPage = page.url().includes('login');
|
||
|
|
|
||
|
|
console.log('Error message visible:', errorMessage);
|
||
|
|
console.log('Still on login page:', stillOnLoginPage);
|
||
|
|
|
||
|
|
expect(errorMessage || stillOnLoginPage).toBeTruthy();
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Invalid credentials test completed');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should validate required fields', async ({ page }) => {
|
||
|
|
severity('normal');
|
||
|
|
description('Verify that required field validation works correctly');
|
||
|
|
|
||
|
|
console.log('Testing required field validation...');
|
||
|
|
|
||
|
|
await test.step('Click login without filling fields', async () => {
|
||
|
|
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
|
||
|
|
await page.waitForTimeout(1000);
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Verify validation or staying on login page', async () => {
|
||
|
|
const stillOnLoginPage = page.url().includes('login');
|
||
|
|
console.log('Still on login page:', stillOnLoginPage);
|
||
|
|
expect(stillOnLoginPage).toBeTruthy();
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Required field validation test completed');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('should successfully logout', async ({ page }) => {
|
||
|
|
test.setTimeout(90000);
|
||
|
|
severity('critical');
|
||
|
|
description('Verify that clicking Logout redirects to login page and clears session');
|
||
|
|
|
||
|
|
// First login
|
||
|
|
await test.step('Login first', async () => {
|
||
|
|
await page.fill('input[type="email"], input[name="email"], input[placeholder*="email" i]', TEST_CREDENTIALS.username);
|
||
|
|
await page.fill('input[type="password"], input[name="password"]', TEST_CREDENTIALS.password);
|
||
|
|
await page.click('button[type="submit"], button:has-text("Login"), button:has-text("Sign in")');
|
||
|
|
await page.waitForURL('**/dashboard**', { timeout: 30000 }).catch(() => {});
|
||
|
|
await page.waitForLoadState('domcontentloaded').catch(() => {});
|
||
|
|
console.log('✓ Logged in, URL:', page.url());
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Click Logout', async () => {
|
||
|
|
const logoutLink = page.locator('text=Logout').first();
|
||
|
|
|
||
|
|
if (!(await logoutLink.isVisible({ timeout: 2500 }).catch(() => false))) {
|
||
|
|
// Mobile layout might hide logout under a menu; try opening it.
|
||
|
|
const openMenu = page.locator('button[aria-label="Open menu"], button[aria-label="Open navigation"], button:has-text("Menu"), button:has-text("Open")');
|
||
|
|
if (await openMenu.isVisible({ timeout: 2500 }).catch(() => false)) {
|
||
|
|
await openMenu.click();
|
||
|
|
await page.waitForTimeout(1000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
await logoutLink.waitFor({ state: 'visible', timeout: 10000 });
|
||
|
|
await logoutLink.click();
|
||
|
|
await page.waitForTimeout(3000);
|
||
|
|
console.log('✓ Logout clicked');
|
||
|
|
});
|
||
|
|
|
||
|
|
await test.step('Verify redirected to login page', async () => {
|
||
|
|
await page.waitForURL('**/login**', { timeout: 10000 }).catch(() => {});
|
||
|
|
const currentUrl = page.url();
|
||
|
|
console.log('URL after logout:', currentUrl);
|
||
|
|
expect(currentUrl).toContain('login');
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('✓ Logout test completed');
|
||
|
|
});
|
||
|
|
});
|