DB, Collections, Search
This commit is contained in:
188
e2e/auth.spec.ts
Normal file
188
e2e/auth.spec.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Authentication Flows', () => {
|
||||
// Run tests serially since they depend on shared state (registered user)
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
// Generate unique email for each test run to avoid conflicts
|
||||
// Include random suffix to prevent collisions between parallel browser runs
|
||||
const timestamp = Date.now()
|
||||
const randomSuffix = Math.random().toString(36).substring(7)
|
||||
const testEmail = `test${timestamp}${randomSuffix}@example.com`
|
||||
const testPassword = 'password123'
|
||||
const testName = 'Test User'
|
||||
|
||||
test.describe('Registration', () => {
|
||||
test('should display registration form', async ({ page }) => {
|
||||
await page.goto('/register')
|
||||
|
||||
await expect(page.locator('h2')).toContainText('Create Account')
|
||||
await expect(page.getByLabel('Email')).toBeVisible()
|
||||
await expect(page.getByLabel('Password')).toBeVisible()
|
||||
await expect(page.getByLabel('Name')).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: 'Create Account' })).toBeVisible()
|
||||
})
|
||||
|
||||
test('should successfully register a new user', async ({ page }) => {
|
||||
await page.goto('/register')
|
||||
|
||||
// Fill out registration form
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByLabel('Name').fill(testName)
|
||||
|
||||
// Submit form
|
||||
await page.getByRole('button', { name: 'Create Account' }).click()
|
||||
|
||||
// Should redirect to login page
|
||||
await page.waitForURL(/\/login/, { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show error for duplicate email', async ({ page }) => {
|
||||
await page.goto('/register')
|
||||
|
||||
// Try to register with existing email
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByLabel('Name').fill(testName)
|
||||
|
||||
await page.getByRole('button', { name: 'Create Account' }).click()
|
||||
|
||||
// Should show error message (from server)
|
||||
await expect(page.getByText(/already exists|error/i)).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should have link to login page', async ({ page }) => {
|
||||
await page.goto('/register')
|
||||
|
||||
const loginLink = page.getByRole('link', { name: 'Sign in' })
|
||||
await expect(loginLink).toBeVisible()
|
||||
|
||||
await loginLink.click()
|
||||
await expect(page).toHaveURL('/login')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Login', () => {
|
||||
test('should display login form', async ({ page }) => {
|
||||
await page.goto('/login')
|
||||
|
||||
await expect(page.locator('h2')).toContainText('Sign In')
|
||||
await expect(page.getByLabel('Email')).toBeVisible()
|
||||
await expect(page.getByLabel('Password')).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible()
|
||||
})
|
||||
|
||||
test('should successfully login with valid credentials', async ({ page }) => {
|
||||
await page.goto('/login')
|
||||
|
||||
// Fill out login form
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
|
||||
// Submit form
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
|
||||
// Should redirect to dashboard
|
||||
await page.waitForURL('/dashboard', { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
await page.goto('/login')
|
||||
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill('wrongpassword')
|
||||
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
|
||||
// Should show error message
|
||||
await expect(page.getByText(/error/i)).toBeVisible({ timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should have link to registration page', async ({ page }) => {
|
||||
await page.goto('/login')
|
||||
|
||||
const registerLink = page.getByRole('link', { name: 'Sign up' })
|
||||
await expect(registerLink).toBeVisible()
|
||||
|
||||
await registerLink.click()
|
||||
await expect(page).toHaveURL('/register')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Logout', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Log in before each test
|
||||
await page.goto('/login')
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
await page.waitForURL('/dashboard', { timeout: 10000 })
|
||||
})
|
||||
|
||||
test('should successfully logout', async ({ page }) => {
|
||||
// Find and click logout button
|
||||
const logoutButton = page.getByRole('button', { name: /logout|sign out/i })
|
||||
await logoutButton.click()
|
||||
|
||||
// Should redirect to login or home page
|
||||
await page.waitForURL(/\/(login|$)/, { timeout: 5000 })
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Protected Routes', () => {
|
||||
test('should redirect to login when accessing dashboard without authentication', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/dashboard')
|
||||
|
||||
// Should redirect to login page
|
||||
await page.waitForURL('/login', { timeout: 5000 })
|
||||
})
|
||||
|
||||
test('should redirect to login when accessing settings without authentication', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/settings')
|
||||
|
||||
// Should redirect to login page
|
||||
await page.waitForURL('/login', { timeout: 5000 })
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Session Persistence', () => {
|
||||
test('should maintain session across page reloads', async ({ page }) => {
|
||||
// Login
|
||||
await page.goto('/login')
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
await page.waitForURL('/dashboard', { timeout: 10000 })
|
||||
|
||||
// Reload page
|
||||
await page.reload()
|
||||
|
||||
// Should still be on dashboard
|
||||
await expect(page).toHaveURL('/dashboard')
|
||||
})
|
||||
|
||||
test('should maintain session when navigating between pages', async ({
|
||||
page,
|
||||
}) => {
|
||||
// Login
|
||||
await page.goto('/login')
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
await page.waitForURL('/dashboard', { timeout: 10000 })
|
||||
|
||||
// Navigate to settings
|
||||
await page.goto('/settings')
|
||||
await expect(page).toHaveURL('/settings')
|
||||
|
||||
// Navigate back to dashboard
|
||||
await page.goto('/dashboard')
|
||||
await expect(page).toHaveURL('/dashboard')
|
||||
})
|
||||
})
|
||||
})
|
||||
17
e2e/global-setup.ts
Normal file
17
e2e/global-setup.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { execSync } from 'child_process'
|
||||
|
||||
async function globalSetup() {
|
||||
console.log('Running database seed before E2E tests...')
|
||||
try {
|
||||
execSync('npx prisma db seed', {
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
})
|
||||
console.log('Database seed completed successfully')
|
||||
} catch (error) {
|
||||
console.error('Warning: Database seed failed. Tests may fail if seed data is missing.')
|
||||
console.error('Make sure Docker Compose is running and the database is accessible.')
|
||||
}
|
||||
}
|
||||
|
||||
export default globalSetup
|
||||
92
e2e/settings.spec.ts
Normal file
92
e2e/settings.spec.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Settings Page', () => {
|
||||
// Run tests serially since they depend on shared state
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
// Generate unique email for each test run
|
||||
// Include random suffix to prevent collisions between parallel browser runs
|
||||
const timestamp = Date.now()
|
||||
const randomSuffix = Math.random().toString(36).substring(7)
|
||||
const testEmail = `settings${timestamp}${randomSuffix}@example.com`
|
||||
const testPassword = 'password123'
|
||||
const testName = 'Settings Test User'
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Try to register a new user (may already exist from previous test)
|
||||
await page.goto('/register')
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByLabel('Name').fill(testName)
|
||||
await page.getByRole('button', { name: 'Create Account' }).click()
|
||||
|
||||
// Wait for either redirect to login OR error message (if user exists)
|
||||
await Promise.race([
|
||||
page.waitForURL(/\/login/, { timeout: 10000 }),
|
||||
page.waitForSelector('text=/already exists|error/i', { timeout: 10000 }),
|
||||
])
|
||||
|
||||
// Navigate to login and login
|
||||
await page.goto('/login')
|
||||
await page.getByLabel('Email').fill(testEmail)
|
||||
await page.getByLabel('Password').fill(testPassword)
|
||||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||||
await page.waitForURL('/dashboard', { timeout: 10000 })
|
||||
|
||||
// Navigate to settings
|
||||
await page.goto('/settings')
|
||||
await page.waitForLoadState('networkidle')
|
||||
})
|
||||
|
||||
test.describe('Profile Settings', () => {
|
||||
test('should display profile settings form', async ({ page }) => {
|
||||
// Should have profile section with name and email fields
|
||||
await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible()
|
||||
await expect(page.getByText('Name')).toBeVisible()
|
||||
await expect(page.getByText('Email')).toBeVisible()
|
||||
// Check textboxes exist
|
||||
const textboxes = page.getByRole('textbox')
|
||||
await expect(textboxes.first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('should display current user information', async ({ page }) => {
|
||||
// Should show current values in the form
|
||||
// Profile section is the first form, so first two textboxes are name and email
|
||||
const textboxes = page.getByRole('textbox')
|
||||
await expect(textboxes.first()).toHaveValue(testName)
|
||||
await expect(textboxes.nth(1)).toHaveValue(testEmail)
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Password Change', () => {
|
||||
test('should display password change form', async ({ page }) => {
|
||||
// Should have password section
|
||||
await expect(page.getByRole('heading', { name: 'Change Password' })).toBeVisible()
|
||||
await expect(page.getByText('Current Password')).toBeVisible()
|
||||
await expect(page.getByText('New Password')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('User Preferences', () => {
|
||||
test('should display preferences form', async ({ page }) => {
|
||||
// Should have preferences section
|
||||
await expect(page.getByRole('heading', { name: 'Learning Preferences' })).toBeVisible()
|
||||
// Check for slider labels (they include values)
|
||||
await expect(page.getByText(/Cards Per Session/)).toBeVisible()
|
||||
await expect(page.getByText(/Daily Goal/)).toBeVisible()
|
||||
})
|
||||
|
||||
test('should display default preferences', async ({ page }) => {
|
||||
// Should show default values displayed in labels
|
||||
await expect(page.getByText(/Cards Per Session:.*20/)).toBeVisible()
|
||||
await expect(page.getByText(/Daily Goal:.*50/)).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Navigation', () => {
|
||||
test('should have navigation options', async ({ page }) => {
|
||||
// Check that we're on settings page and can navigate
|
||||
await expect(page).toHaveURL('/settings')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user