# frozen_string_literal: true require 'playwright' RSpec.describe 'Authentication', type: :playwright do # Use RSpec's around hook to manage Playwright lifecycle around do |example| Playwright.create(playwright_cli_executable_path: './node_modules/.bin/playwright') do |playwright| playwright.chromium.launch(headless: true) do |browser| @browser = browser example.run end end end before(:each) do @context = @browser.new_context(viewport: { width: 1280, height: 800 }) @page = @context.new_page end after(:each) do @context.close end describe 'Login Form Display' do it 'displays login page at /login' do @page.goto('http://localhost:2300/login') expect(@page.locator('h1').text_content).to include('Login') end it 'has email and password input fields' do @page.goto('http://localhost:2300/login') email_input = @page.locator('input[type="email"]') password_input = @page.locator('input[type="password"]') expect(email_input.count).to be > 0 expect(password_input.count).to be > 0 end it 'has a login form with POST method' do @page.goto('http://localhost:2300/login') form = @page.locator('form[method="POST"]') expect(form.count).to be > 0 end it 'includes CSRF token in login form' do @page.goto('http://localhost:2300/login') csrf_input = @page.locator('input[name="_csrf_token"]') expect(csrf_input.count).to be > 0 expect(csrf_input.input_value).not_to be_empty end end describe 'Login Flow' do before(:all) do # Admin user credentials for testing @admin_email = 'david@dhg.lol' @admin_password = 'admin' end it 'successfully logs in with valid credentials' do @page.goto('http://localhost:2300/login') # Fill in login form @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) # Submit form @page.locator('button[type="submit"]').click # Should redirect to rankings page expect(@page.url).to include('/') end it 'shows success message after login' do @page.goto('http://localhost:2300/login') # Fill in login form @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) # Submit form @page.locator('button[type="submit"]').click # Should redirect to rankings page expect(@page.url).to include('/') end it 'redirects to admin dashboard when logged in' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # After login, user is redirected to rankings # Then navigate to admin @page.goto('http://localhost:2300/admin') # Should see admin dashboard expect(@page.locator('h1').text_content).to include('Admin Dashboard') end end describe 'Failed Login Scenarios' do it 'rejects invalid email' do @page.goto('http://localhost:2300/login') @page.locator('input[type="email"]').fill('invalid@example.com') @page.locator('input[type="password"]').fill('wrongpassword') @page.locator('button[type="submit"]').click # Should show error message page_content = @page.content expect(page_content).to include('Invalid email or password') end it 'rejects invalid password' do @page.goto('http://localhost:2300/login') @page.locator('input[type="email"]').fill('david@dhg.lol') @page.locator('input[type="password"]').fill('wrongpassword') @page.locator('button[type="submit"]').click # Should show error message page_content = @page.content expect(page_content).to include('Invalid email or password') end it 'requires email field' do @page.goto('http://localhost:2300/login') @page.locator('input[type="password"]').fill('somepassword') @page.locator('button[type="submit"]').click # Browser validation should prevent submission # Or server should handle empty email page_content = @page.content # Either an error or the form should remain expect(page_content).to include('Login') end it 'requires password field' do @page.goto('http://localhost:2300/login') @page.locator('input[type="email"]').fill('david@dhg.lol') @page.locator('button[type="submit"]').click # Browser validation should prevent submission page_content = @page.content expect(page_content).to include('Login') end end describe 'Session Management' do before(:all) do @admin_email = 'david@dhg.lol' @admin_password = 'admin' end it 'creates session after successful login' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Check that session cookie is set cookies = @context.cookies session_cookie = cookies.find { |c| c['name'] == 'rack.session' } expect(session_cookie).not_to be_nil expect(session_cookie['value']).not_to be_empty end it 'persists session across page reloads' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Verify we're logged in @page.goto('http://localhost:2300/admin') expect(@page.locator('h1').text_content).to include('Admin Dashboard') # Reload page @page.reload # Should still be logged in expect(@page.locator('h1').text_content).to include('Admin Dashboard') end end describe 'Logout Functionality' do before(:all) do @admin_email = 'david@dhg.lol' @admin_password = 'admin' end it 'logs out user when clicking logout link' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Verify logged in - check for Logout link in navigation # Navigate to admin to confirm login @page.goto('http://localhost:2300/admin') expect(@page.locator('h1').text_content).to include('Admin Dashboard') # Click logout - use first match since logout may appear in multiple places @page.locator('a[href="/logout"]').first.click # After logout, check we're back at rankings expect(@page.url).to include('/') end it 'clears session after logout' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Navigate to admin to verify logged in @page.goto('http://localhost:2300/admin') expect(@page.locator('h1').text_content).to include('Admin Dashboard') # Logout @page.locator('a[href="/logout"]').first.click # Try to access admin page again - should redirect (not show admin dashboard) @page.goto('http://localhost:2300/admin') # Should not see admin dashboard expect(@page.locator('h1').text_content).not_to include('Admin Dashboard') end end describe 'Authorization Redirects' do before(:all) do @admin_email = 'david@dhg.lol' @admin_password = 'admin' end it 'redirects to rankings when accessing admin page without auth' do @page.goto('http://localhost:2300/admin') # Unauthenticated users are redirected to rankings page expect(@page.url).to include('/') expect(@page.locator('h1').text_content).to include('Rankings') end it 'allows access to admin page after login' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Navigate to admin page @page.goto('http://localhost:2300/admin') # Should see admin dashboard expect(@page.locator('h1').text_content).to include('Admin Dashboard') end it 'redirects to rankings after logout when accessing admin page' do @page.goto('http://localhost:2300/login') # Login @page.locator('input[type="email"]').fill(@admin_email) @page.locator('input[type="password"]').fill(@admin_password) @page.locator('button[type="submit"]').click # Go to admin page @page.goto('http://localhost:2300/admin') expect(@page.url).to include('/admin') # Click logout link in admin page # Note: logout link is in bottom nav on mobile, main nav on desktop @page.locator('a[href="/logout"]').first.click # After logout, try to access admin page again @page.goto('http://localhost:2300/admin') # Should redirect to rankings (unauthenticated users see rankings, not login) expect(@page.url).to include('/') end end end