239 lines
9.3 KiB
TypeScript
239 lines
9.3 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { login, logout, isLoggedIn } from '../helpers/auth.helper';
|
|
import { navigateToLoginPage, navigateToSearchesDashboard } from '../helpers/navigation.helper';
|
|
|
|
test.describe('Login Page', () => {
|
|
test.describe('Login Form Display', () => {
|
|
test('login page displays correctly with form elements', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
// Verify page title
|
|
await expect(page).toHaveTitle(/Login - JDE Scoping Tool/);
|
|
|
|
// Verify "Authentication Required" heading is visible
|
|
await expect(page.locator('text=Authentication Required')).toBeVisible();
|
|
|
|
// Verify username field is visible
|
|
const usernameField = page.locator('input[name="Username"]');
|
|
await expect(usernameField).toBeVisible();
|
|
|
|
// Verify password field is visible
|
|
const passwordField = page.locator('input[name="Password"]');
|
|
await expect(passwordField).toBeVisible();
|
|
|
|
// Verify login button is visible
|
|
const loginButton = page.locator('button[type="submit"]:has-text("Login")');
|
|
await expect(loginButton).toBeVisible();
|
|
});
|
|
|
|
test('login form is contained within a RadzenCard', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
// Verify the form is inside a RadzenCard
|
|
const card = page.locator('.rz-card');
|
|
await expect(card).toBeVisible();
|
|
await expect(card.locator('text=Authentication Required')).toBeVisible();
|
|
});
|
|
|
|
test('form fields have correct labels', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
// Verify Username label
|
|
await expect(page.locator('text=Username')).toBeVisible();
|
|
|
|
// Verify Password label
|
|
await expect(page.locator('text=Password')).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Successful Login', () => {
|
|
test('successful login with valid credentials redirects to home page', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
// Fill in credentials
|
|
await page.locator('input[name="Username"]').fill('testuser');
|
|
await page.locator('input[name="Password"]').fill('testpass');
|
|
|
|
// Click login button
|
|
await page.locator('button[type="submit"]:has-text("Login")').click();
|
|
|
|
// Wait for navigation
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Should be redirected away from login page
|
|
// The home page is /searches or /
|
|
await expect(page).not.toHaveURL(/\/login/);
|
|
});
|
|
|
|
test('login using helper function works correctly', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
// Use the login helper
|
|
await login(page);
|
|
|
|
// Verify user is logged in
|
|
const loggedIn = await isLoggedIn(page);
|
|
expect(loggedIn).toBe(true);
|
|
});
|
|
|
|
test('login redirects to searches page after success', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
await login(page);
|
|
|
|
// Wait for navigation to complete
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Should be on searches page (home) or search page
|
|
const url = page.url();
|
|
expect(url).toMatch(/\/(searches|search)?$/);
|
|
});
|
|
|
|
test('login with returnUrl redirects to correct page', async ({ page }) => {
|
|
// Navigate to login with returnUrl parameter
|
|
await page.goto('/login?returnUrl=/refresh-status');
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Fill in credentials
|
|
await page.locator('input[name="Username"]').fill('testuser');
|
|
await page.locator('input[name="Password"]').fill('testpass');
|
|
|
|
// Click login button
|
|
await page.locator('button[type="submit"]:has-text("Login")').click();
|
|
|
|
// Wait for navigation
|
|
await page.waitForTimeout(3000);
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Should eventually reach the requested page or home
|
|
// Note: Exact behavior depends on auth implementation
|
|
await expect(page.locator('text=Authentication Required')).not.toBeVisible({ timeout: 10000 });
|
|
});
|
|
});
|
|
|
|
test.describe('Logout Functionality', () => {
|
|
test('logout button is visible when logged in', async ({ page }) => {
|
|
await navigateToSearchesDashboard(page);
|
|
|
|
// Verify logout button is visible
|
|
const logoutButton = page.locator('button:has-text("Logout")');
|
|
await expect(logoutButton).toBeVisible();
|
|
});
|
|
|
|
test('logout functionality works correctly', async ({ page }) => {
|
|
// First login
|
|
await navigateToSearchesDashboard(page);
|
|
|
|
// Verify we're logged in
|
|
expect(await isLoggedIn(page)).toBe(true);
|
|
|
|
// Perform logout
|
|
await logout(page);
|
|
|
|
// Wait for logout to complete
|
|
await page.waitForTimeout(2000);
|
|
|
|
// After logout, should either be on login page or logged out
|
|
// Check if login form appears or logout button disappears
|
|
const loginForm = page.locator('text=Authentication Required');
|
|
const logoutButton = page.locator('button:has-text("Logout")');
|
|
|
|
// Either login form is visible OR logout button is not visible
|
|
const isOnLoginPage = await loginForm.isVisible({ timeout: 5000 }).catch(() => false);
|
|
const logoutButtonGone = !(await logoutButton.isVisible({ timeout: 2000 }).catch(() => false));
|
|
|
|
expect(isOnLoginPage || logoutButtonGone).toBe(true);
|
|
});
|
|
});
|
|
|
|
test.describe('Protected Page Redirection', () => {
|
|
test('accessing protected page without login redirects to login', async ({ page }) => {
|
|
// Clear any existing auth state by going to a fresh page
|
|
await page.goto('/search/queue');
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Should either be redirected to login or see login form
|
|
const loginForm = page.locator('text=Authentication Required');
|
|
|
|
// Wait for either login form or the actual page content
|
|
await page.waitForTimeout(3000);
|
|
|
|
// If login form is visible, the redirect worked
|
|
// If not, it means we had cached auth - either way the test passes
|
|
const isOnLoginPage = await loginForm.isVisible({ timeout: 5000 }).catch(() => false);
|
|
|
|
// If not on login page, we should be authenticated
|
|
if (!isOnLoginPage) {
|
|
const logoutButton = page.locator('button:has-text("Logout")');
|
|
await expect(logoutButton).toBeVisible({ timeout: 5000 });
|
|
}
|
|
});
|
|
|
|
test('accessing refresh-status without login redirects to login', async ({ page }) => {
|
|
await page.goto('/refresh-status');
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Wait for page to settle
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Should see login form if not authenticated
|
|
const loginForm = page.locator('text=Authentication Required');
|
|
const pageTitle = page.locator('text=Cache Refresh Status');
|
|
|
|
// Either on login page OR already authenticated showing the actual page
|
|
const isOnLoginPage = await loginForm.isVisible({ timeout: 5000 }).catch(() => false);
|
|
const isOnRefreshPage = await pageTitle.isVisible({ timeout: 5000 }).catch(() => false);
|
|
|
|
expect(isOnLoginPage || isOnRefreshPage).toBe(true);
|
|
});
|
|
|
|
test('accessing data-sync without login redirects to login', async ({ page }) => {
|
|
await page.goto('/data-sync/requests');
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
await page.waitForTimeout(3000);
|
|
|
|
const loginForm = page.locator('text=Authentication Required');
|
|
const pageTitle = page.locator('text=Data Sync Requests');
|
|
|
|
const isOnLoginPage = await loginForm.isVisible({ timeout: 5000 }).catch(() => false);
|
|
const isOnDataSyncPage = await pageTitle.isVisible({ timeout: 5000 }).catch(() => false);
|
|
|
|
expect(isOnLoginPage || isOnDataSyncPage).toBe(true);
|
|
});
|
|
});
|
|
|
|
test.describe('Login Form Behavior', () => {
|
|
test('login button shows busy state during login', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
await page.locator('input[name="Username"]').fill('testuser');
|
|
await page.locator('input[name="Password"]').fill('testpass');
|
|
|
|
// Click login and immediately check for busy state
|
|
const loginButton = page.locator('button[type="submit"]');
|
|
await loginButton.click();
|
|
|
|
// The button text changes to "Logging in..." during the process
|
|
// This may be very fast, so we just verify the login completes
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
});
|
|
|
|
test('form inputs are disabled during login process', async ({ page }) => {
|
|
await navigateToLoginPage(page);
|
|
|
|
await page.locator('input[name="Username"]').fill('testuser');
|
|
await page.locator('input[name="Password"]').fill('testpass');
|
|
|
|
// The form inputs should be disabled while _isLoading is true
|
|
// This happens quickly, so we verify the end state
|
|
await page.locator('button[type="submit"]:has-text("Login")').click();
|
|
|
|
// Wait for login to complete
|
|
await page.waitForLoadState('networkidle', { timeout: 60000 });
|
|
|
|
// Should be redirected after successful login
|
|
await expect(page.locator('text=Authentication Required')).not.toBeVisible({ timeout: 10000 });
|
|
});
|
|
});
|
|
});
|