Initial commit - production deployment
This commit is contained in:
121
frontend/tests/auth/login.spec.ts
Normal file
121
frontend/tests/auth/login.spec.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { login, logout, TEST_USER } from '../helpers/auth';
|
||||
import { acceptCookieConsent } from '../helpers/utils';
|
||||
|
||||
test.describe('Login Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Start at login page
|
||||
await page.goto('/login');
|
||||
// Accept cookie consent if present
|
||||
await acceptCookieConsent(page);
|
||||
});
|
||||
|
||||
test('should display login form', async ({ page }) => {
|
||||
// Verify login page elements are visible (support both English and Spanish)
|
||||
await expect(page.getByLabel(/email|correo/i)).toBeVisible();
|
||||
await expect(page.getByRole('textbox', { name: /password|contraseña/i })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: /log in|sign in|login|acceder/i })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should successfully login with valid credentials', async ({ page }) => {
|
||||
// Fill in credentials
|
||||
await page.getByLabel(/email|correo/i).fill(TEST_USER.email);
|
||||
await page.getByRole('textbox', { name: /password|contraseña/i }).fill(TEST_USER.password);
|
||||
|
||||
// Click login button
|
||||
await page.getByRole('button', { name: /log in|sign in|login|acceder/i }).click();
|
||||
|
||||
// Should redirect to dashboard or app
|
||||
await expect(page).toHaveURL(/\/(app|dashboard)/, { timeout: 10000 });
|
||||
|
||||
// Verify we see dashboard content
|
||||
await expect(page.locator('body')).toContainText(/dashboard|panel de control/i);
|
||||
});
|
||||
|
||||
test('should show error with invalid email', async ({ page }) => {
|
||||
// Fill in invalid credentials
|
||||
await page.getByLabel(/email|correo/i).fill('invalid@email.com');
|
||||
await page.getByRole('textbox', { name: /password|contraseña/i }).fill('wrongpassword');
|
||||
|
||||
// Click login button
|
||||
await page.getByRole('button', { name: /log in|sign in|login|acceder/i }).click();
|
||||
|
||||
// Should show error message
|
||||
await expect(page.locator('body')).toContainText(/invalid|incorrect|error|credenciales/i, {
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
// Should stay on login page
|
||||
await expect(page).toHaveURL(/\/login/);
|
||||
});
|
||||
|
||||
test('should show validation error for empty email', async ({ page }) => {
|
||||
// Try to submit without email
|
||||
await page.getByRole('textbox', { name: /password|contraseña/i }).fill('somepassword');
|
||||
await page.getByRole('button', { name: /log in|sign in|login|acceder/i }).click();
|
||||
|
||||
// Should show validation error (either inline or toast)
|
||||
const bodyText = await page.locator('body').textContent();
|
||||
expect(bodyText).toMatch(/required|obligatorio|necesario/i);
|
||||
});
|
||||
|
||||
test('should show validation error for empty password', async ({ page }) => {
|
||||
// Try to submit without password
|
||||
await page.getByLabel(/email|correo/i).fill('test@example.com');
|
||||
await page.getByRole('button', { name: /log in|sign in|login|acceder/i }).click();
|
||||
|
||||
// Should show validation error
|
||||
const bodyText = await page.locator('body').textContent();
|
||||
expect(bodyText).toMatch(/required|obligatorio|necesario/i);
|
||||
});
|
||||
|
||||
test('should toggle password visibility', async ({ page }) => {
|
||||
const passwordInput = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
|
||||
// Initially should be password type
|
||||
await expect(passwordInput).toHaveAttribute('type', 'password');
|
||||
|
||||
// Look for toggle button (eye icon, "show password", etc.)
|
||||
const toggleButton = page.getByRole('button', { name: /show|mostrar.*password|contraseña/i });
|
||||
|
||||
const isToggleVisible = await toggleButton.isVisible({ timeout: 2000 }).catch(() => false);
|
||||
|
||||
if (isToggleVisible) {
|
||||
await toggleButton.click();
|
||||
|
||||
// Should change to text type
|
||||
await expect(passwordInput).toHaveAttribute('type', 'text');
|
||||
|
||||
// Toggle back
|
||||
await toggleButton.click();
|
||||
await expect(passwordInput).toHaveAttribute('type', 'password');
|
||||
}
|
||||
});
|
||||
|
||||
test('should have link to registration page', async ({ page }) => {
|
||||
// Look for register/signup button or link
|
||||
const registerButton = page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i });
|
||||
const registerLink = page.getByRole('link', { name: /register|sign up|crear cuenta|registrar/i });
|
||||
|
||||
const isButtonVisible = await registerButton.isVisible({ timeout: 2000 }).catch(() => false);
|
||||
const isLinkVisible = await registerLink.isVisible({ timeout: 2000 }).catch(() => false);
|
||||
|
||||
if (isLinkVisible) {
|
||||
await expect(registerLink).toHaveAttribute('href', /\/register/);
|
||||
} else if (isButtonVisible) {
|
||||
// If it's a button, just verify it exists
|
||||
await expect(registerButton).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('should redirect to app if already logged in', async ({ page, context }) => {
|
||||
// First login
|
||||
await login(page, TEST_USER);
|
||||
|
||||
// Try to go to login page again
|
||||
await page.goto('/login');
|
||||
|
||||
// Should redirect to app/dashboard
|
||||
await expect(page).toHaveURL(/\/(app|dashboard)/, { timeout: 5000 });
|
||||
});
|
||||
});
|
||||
94
frontend/tests/auth/logout.spec.ts
Normal file
94
frontend/tests/auth/logout.spec.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { acceptCookieConsent } from '../helpers/utils';
|
||||
|
||||
test.describe('Logout Flow', () => {
|
||||
// Use authenticated state for these tests
|
||||
test.use({ storageState: 'tests/.auth/user.json' });
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Accept cookie consent if present on any page navigation
|
||||
await acceptCookieConsent(page);
|
||||
});
|
||||
|
||||
test('should successfully logout', async ({ page }) => {
|
||||
// Navigate to dashboard
|
||||
await page.goto('/app/dashboard');
|
||||
await acceptCookieConsent(page);
|
||||
|
||||
// Verify we're logged in
|
||||
await expect(page.locator('body')).toContainText(/dashboard|panel de control/i);
|
||||
|
||||
// Look for user menu or logout button
|
||||
// Try different common patterns
|
||||
const userMenuButton = page.getByRole('button', { name: /user|account|profile|usuario|cuenta/i }).first();
|
||||
|
||||
if (await userMenuButton.isVisible().catch(() => false)) {
|
||||
await userMenuButton.click();
|
||||
|
||||
// Wait for menu to open
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Click logout button
|
||||
const logoutButton = page.getByRole('button', { name: /log out|logout|sign out|cerrar sesión/i });
|
||||
await logoutButton.click();
|
||||
|
||||
// Should redirect to login page
|
||||
await expect(page).toHaveURL(/\/(login|$)/, { timeout: 10000 });
|
||||
|
||||
// Verify we're logged out (check for login form)
|
||||
await acceptCookieConsent(page);
|
||||
await expect(page.getByLabel(/email|correo/i)).toBeVisible();
|
||||
});
|
||||
|
||||
test('should not access protected routes after logout', async ({ page }) => {
|
||||
// Navigate to dashboard
|
||||
await page.goto('/app/dashboard');
|
||||
|
||||
// Logout
|
||||
const userMenuButton = page.getByRole('button', { name: /user|account|profile|usuario|cuenta/i }).first();
|
||||
|
||||
if (await userMenuButton.isVisible().catch(() => false)) {
|
||||
await userMenuButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: /log out|logout|sign out|cerrar sesión/i }).click();
|
||||
|
||||
// Wait for redirect
|
||||
await page.waitForURL(/\/(login|$)/);
|
||||
|
||||
// Try to access protected route
|
||||
await page.goto('/app/dashboard');
|
||||
|
||||
// Should redirect back to login
|
||||
await expect(page).toHaveURL(/\/login/, { timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should clear user data after logout', async ({ page, context }) => {
|
||||
// Navigate to dashboard
|
||||
await page.goto('/app/dashboard');
|
||||
|
||||
// Logout
|
||||
const userMenuButton = page.getByRole('button', { name: /user|account|profile|usuario|cuenta/i }).first();
|
||||
|
||||
if (await userMenuButton.isVisible().catch(() => false)) {
|
||||
await userMenuButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: /log out|logout|sign out|cerrar sesión/i }).click();
|
||||
|
||||
// Wait for redirect
|
||||
await page.waitForURL(/\/(login|$)/);
|
||||
|
||||
// Check that authentication tokens are cleared
|
||||
const cookies = await context.cookies();
|
||||
const authCookies = cookies.filter((cookie) =>
|
||||
cookie.name.toLowerCase().includes('token') || cookie.name.toLowerCase().includes('auth')
|
||||
);
|
||||
|
||||
// Auth cookies should be removed or expired
|
||||
expect(authCookies.length).toBe(0);
|
||||
});
|
||||
});
|
||||
186
frontend/tests/auth/register.spec.ts
Normal file
186
frontend/tests/auth/register.spec.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { generateTestId, acceptCookieConsent } from '../helpers/utils';
|
||||
|
||||
test.describe('Registration Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Start at registration page
|
||||
await page.goto('/register');
|
||||
// Accept cookie consent if present
|
||||
await acceptCookieConsent(page);
|
||||
});
|
||||
|
||||
test('should display registration form', async ({ page }) => {
|
||||
// Verify registration form elements (support both English and Spanish)
|
||||
await expect(page.getByLabel(/email|correo/i)).toBeVisible();
|
||||
await expect(page.getByRole('textbox', { name: /password|contraseña/i }).first()).toBeVisible();
|
||||
|
||||
// Look for submit button
|
||||
const submitButton = page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i });
|
||||
await expect(submitButton).toBeVisible();
|
||||
});
|
||||
|
||||
test('should successfully register a new user', async ({ page }) => {
|
||||
// Generate unique test credentials
|
||||
const testEmail = `test-${generateTestId()}@bakery.com`;
|
||||
const testPassword = 'Test123!@#Password';
|
||||
|
||||
// Fill in registration form
|
||||
await page.getByLabel(/email|correo/i).fill(testEmail);
|
||||
|
||||
// Find password fields
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
await passwordFields.first().fill(testPassword);
|
||||
|
||||
// If there's a confirm password field
|
||||
const count = await passwordFields.count();
|
||||
if (count > 1) {
|
||||
await passwordFields.nth(1).fill(testPassword);
|
||||
}
|
||||
|
||||
// Fill additional fields if they exist
|
||||
const nameField = page.getByLabel(/name|nombre/i);
|
||||
if (await nameField.isVisible().catch(() => false)) {
|
||||
await nameField.fill('Test User');
|
||||
}
|
||||
|
||||
const companyField = page.getByLabel(/company|bakery|panadería/i);
|
||||
if (await companyField.isVisible().catch(() => false)) {
|
||||
await companyField.fill('Test Bakery');
|
||||
}
|
||||
|
||||
// Accept terms if checkbox exists
|
||||
const termsCheckbox = page.getByLabel(/terms|accept|acepto/i);
|
||||
if (await termsCheckbox.isVisible().catch(() => false)) {
|
||||
await termsCheckbox.check();
|
||||
}
|
||||
|
||||
// Submit form
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should redirect to onboarding or dashboard
|
||||
await expect(page).toHaveURL(/\/(app|dashboard|onboarding)/, { timeout: 15000 });
|
||||
});
|
||||
|
||||
test('should show validation error for invalid email format', async ({ page }) => {
|
||||
// Fill in invalid email
|
||||
await page.getByLabel(/email|correo/i).fill('invalid-email');
|
||||
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
await passwordFields.first().fill('ValidPassword123!');
|
||||
|
||||
// Submit
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should show email validation error
|
||||
await expect(page.locator('body')).toContainText(/valid email|email válido|formato/i, {
|
||||
timeout: 5000,
|
||||
});
|
||||
});
|
||||
|
||||
test('should show error for weak password', async ({ page }) => {
|
||||
const testEmail = `test-${generateTestId()}@bakery.com`;
|
||||
|
||||
await page.getByLabel(/email|correo/i).fill(testEmail);
|
||||
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
await passwordFields.first().fill('123'); // Weak password
|
||||
|
||||
const count = await passwordFields.count();
|
||||
if (count > 1) {
|
||||
await passwordFields.nth(1).fill('123');
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should show password strength error
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/password.*strong|contraseña.*fuerte|weak|débil|minimum|mínimo/i,
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
});
|
||||
|
||||
test('should show error when passwords do not match', async ({ page }) => {
|
||||
const testEmail = `test-${generateTestId()}@bakery.com`;
|
||||
|
||||
await page.getByLabel(/email|correo/i).fill(testEmail);
|
||||
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
|
||||
// Only test if there are multiple password fields (password + confirm)
|
||||
const count = await passwordFields.count();
|
||||
if (count > 1) {
|
||||
await passwordFields.first().fill('Password123!');
|
||||
await passwordFields.nth(1).fill('DifferentPassword123!');
|
||||
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should show mismatch error
|
||||
await expect(page.locator('body')).toContainText(/match|coincide|igual/i, {
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('should show error for already registered email', async ({ page }) => {
|
||||
// Try to register with an email that's already in use
|
||||
await page.getByLabel(/email|correo/i).fill('existing@bakery.com');
|
||||
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
await passwordFields.first().fill('ValidPassword123!');
|
||||
|
||||
const count = await passwordFields.count();
|
||||
if (count > 1) {
|
||||
await passwordFields.nth(1).fill('ValidPassword123!');
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should show error about email already existing
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/already.*exist|ya.*existe|already.*registered|ya.*registrado/i,
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
});
|
||||
|
||||
test('should have link to login page', async ({ page }) => {
|
||||
// Look for login link or button
|
||||
const loginLink = page.getByRole('link', { name: /log in|sign in|iniciar sesión/i });
|
||||
const loginButton = page.getByRole('button', { name: /log in|sign in|iniciar sesión/i });
|
||||
|
||||
const isLinkVisible = await loginLink.isVisible({ timeout: 2000 }).catch(() => false);
|
||||
const isButtonVisible = await loginButton.isVisible({ timeout: 2000 }).catch(() => false);
|
||||
|
||||
if (isLinkVisible) {
|
||||
await expect(loginLink).toHaveAttribute('href', /\/login/);
|
||||
} else if (isButtonVisible) {
|
||||
await expect(loginButton).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('should require acceptance of terms and conditions', async ({ page }) => {
|
||||
const termsCheckbox = page.getByLabel(/terms|accept|acepto/i);
|
||||
|
||||
// Only test if terms checkbox exists
|
||||
if (await termsCheckbox.isVisible().catch(() => false)) {
|
||||
const testEmail = `test-${generateTestId()}@bakery.com`;
|
||||
|
||||
await page.getByLabel(/email|correo/i).fill(testEmail);
|
||||
|
||||
const passwordFields = page.getByRole('textbox', { name: /password|contraseña/i });
|
||||
await passwordFields.first().fill('ValidPassword123!');
|
||||
|
||||
const count = await passwordFields.count();
|
||||
if (count > 1) {
|
||||
await passwordFields.nth(1).fill('ValidPassword123!');
|
||||
}
|
||||
|
||||
// Try to submit without checking terms
|
||||
await page.getByRole('button', { name: /register|sign up|crear cuenta|registrar/i }).click();
|
||||
|
||||
// Should show error or prevent submission
|
||||
await expect(page.locator('body')).toContainText(/terms|accept|acepto|required/i, {
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user