import { test, expect } from '@playwright/test'; test.describe('Session Management', () => { test.describe('Active Sessions', () => { test('can view active sessions', async ({ page }) => { await page.goto('/users/settings'); // Skip if redirected to sudo verification if ((await page.url()).includes('sudo-verify')) { return; } // Look for sessions section const sessionsSection = page.locator( ':has-text("Sessions"), :has-text("Active"), a[href*="sessions"]' ).first(); if (await sessionsSection.isVisible({ timeout: 2000 }).catch(() => false)) { await sessionsSection.click(); await page.waitForTimeout(500); } }); test('shows current session details', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } // Look for session information const sessionInfo = page.locator( ':has-text("Browser"), :has-text("IP"), :has-text("Current")' ).first(); if (await sessionInfo.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(sessionInfo).toBeVisible(); } }); test('shows session creation timestamp', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const timestamp = page.locator( 'time, :has-text("ago"), :has-text("Created")' ).first(); if (await timestamp.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(timestamp).toBeVisible(); } }); test('shows device/browser information', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const deviceInfo = page.locator( ':has-text("Chrome"), :has-text("Firefox"), :has-text("Safari"), :has-text("Mac"), :has-text("Windows")' ).first(); if (await deviceInfo.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(deviceInfo).toBeVisible(); } }); test('shows IP address of session', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } // Look for IP address pattern const ipAddress = page.locator(':has-text(".")').first(); if (await ipAddress.isVisible({ timeout: 2000 }).catch(() => false)) { // IP addresses contain dots await expect(page.locator('body')).toBeVisible(); } }); }); test.describe('Session Termination', () => { test('can revoke other sessions', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const revokeButton = page.locator( 'button:has-text("Revoke"), button:has-text("End"), button:has-text("Terminate")' ).first(); if (await revokeButton.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(revokeButton).toBeVisible(); } }); test('shows confirmation before revoking session', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const revokeButton = page.locator('button:has-text("Revoke")').first(); if (await revokeButton.isVisible({ timeout: 2000 }).catch(() => false)) { await revokeButton.click(); await page.waitForTimeout(300); const confirmation = page.locator( ':has-text("confirm"), :has-text("sure"), [role="dialog"]' ).first(); if (await confirmation.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(confirmation).toBeVisible(); } } }); test('can revoke all other sessions', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const revokeAllButton = page.locator( 'button:has-text("Revoke all"), button:has-text("End all")' ).first(); if (await revokeAllButton.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(revokeAllButton).toBeVisible(); } }); }); test.describe('Session Timeout', () => { test('maintains session while active', async ({ page }) => { await page.goto('/dashboard'); // User should stay logged in await expect(page).toHaveURL(/\/dashboard/); await expect(page.locator('body')).toBeVisible(); }); test('shows session expiry warning', async ({ page }) => { await page.goto('/dashboard'); // In a real scenario, after long inactivity, a warning would appear // This test just verifies the page loads await expect(page.locator('body')).toBeVisible(); }); }); test.describe('Remember Me', () => { test('login page shows remember me option', async ({ page }) => { await page.goto('/users/log-in'); const rememberCheckbox = page.locator( 'input[name*="remember"], input[type="checkbox"]:near(:has-text("Remember"))' ).first(); if (await rememberCheckbox.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(rememberCheckbox).toBeVisible(); } }); }); test.describe('Concurrent Sessions', () => { test('allows multiple active sessions', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } // Look for session list const sessionList = page.locator( 'table, ul, [data-sessions]' ).first(); if (await sessionList.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(sessionList).toBeVisible(); } }); test('distinguishes current session from others', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } // Look for "current" or "this device" indicator const currentIndicator = page.locator( ':has-text("Current"), :has-text("This device"), .badge' ).first(); if (await currentIndicator.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(currentIndicator).toBeVisible(); } }); }); test.describe('Session Security', () => { test('shows last activity time', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } const lastActivity = page.locator( ':has-text("Last active"), :has-text("Last seen"), time' ).first(); if (await lastActivity.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(lastActivity).toBeVisible(); } }); test('shows suspicious session warnings', async ({ page }) => { await page.goto('/users/settings'); if ((await page.url()).includes('sudo-verify')) { return; } // Look for any security warnings const warning = page.locator( '.warning, :has-text("unusual"), :has-text("suspicious")' ).first(); if (await warning.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(warning).toBeVisible(); } }); }); test.describe('Logout', () => { test('can log out from current session', async ({ page }) => { await page.goto('/dashboard'); // Look for logout button const logoutButton = page.locator( 'button:has-text("Log out"), a:has-text("Log out"), button:has-text("Sign out")' ).first(); if (await logoutButton.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(logoutButton).toBeVisible(); } }); test('logout clears session', async ({ page }) => { await page.goto('/dashboard'); const logoutButton = page.locator('button:has-text("Log out"), a:has-text("Log out")').first(); if (await logoutButton.isVisible({ timeout: 2000 }).catch(() => false)) { await logoutButton.click(); await page.waitForTimeout(1000); // Should redirect to login const url = page.url(); if (url.includes('log-in') || url.includes('login')) { await expect(page).toHaveURL(/log-in|login/); } } }); }); });