303 lines
9.4 KiB
Ruby
303 lines
9.4 KiB
Ruby
# 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 |