workspace/projects/gamilit/apps/frontend/e2e/admin-flow.spec.ts
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

449 lines
14 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { loginAsAdmin } from './fixtures/auth-helpers';
import {
waitForPageLoad,
waitForToast,
getByTestId,
waitForLoadingToFinish,
waitForModal,
} from './helpers/page-helpers';
/**
* Admin Flow E2E Tests
*
* Tests the complete admin workflow:
* Login → Admin Portal → Feature Flags → Toggle flag → Verify effect
* Login → Admin Portal → Roles → Edit permissions
*/
test.describe('Admin Flow - Feature Flags', () => {
test.beforeEach(async ({ page }) => {
// Login as admin
await loginAsAdmin(page);
await waitForPageLoad(page);
});
test('should access admin portal and view feature flags', async ({ page }) => {
// Verify we're on admin dashboard
await expect(page).toHaveURL(/\/admin\/dashboard/);
// Navigate to feature flags page
const featureFlagsLink = page.getByRole('link', {
name: /feature flags|banderas|características/i,
});
if (await featureFlagsLink.isVisible({ timeout: 3000 })) {
await featureFlagsLink.click();
} else {
// Try direct navigation
await page.goto('/admin/feature-flags');
}
await waitForPageLoad(page);
// Verify we're on feature flags page
await expect(page).toHaveURL(/\/admin.*feature.*flags|features/);
// Verify feature flags list is visible
const flagsList = page.locator(
'[data-testid="feature-flags-list"], table, [role="table"]'
);
await expect(flagsList.first()).toBeVisible({ timeout: 10000 });
// Verify there are feature flags
const flagRows = page.locator('[data-testid="flag-row"], tr').filter({
hasNot: page.locator('th'),
});
const count = await flagRows.count();
expect(count).toBeGreaterThan(0);
});
test('should toggle a feature flag and verify effect', async ({ page }) => {
// Navigate to feature flags page
await page.goto('/admin/feature-flags');
await waitForPageLoad(page);
// Find a test feature flag (create one if doesn't exist)
const testFlagName = 'test-feature-e2e';
const testFlag = page.locator(`[data-flag="${testFlagName}"], [data-testid="flag-${testFlagName}"]`);
// Get the toggle switch for the flag
let toggleSwitch = page
.locator('[role="switch"], input[type="checkbox"]')
.first();
// Check if specific test flag exists
const hasTestFlag = await testFlag.isVisible({ timeout: 2000 });
if (hasTestFlag) {
toggleSwitch = testFlag.locator('[role="switch"], input[type="checkbox"]');
}
// Get initial state
const initialState = await toggleSwitch.isChecked();
// Toggle the flag
await toggleSwitch.click();
await page.waitForTimeout(500);
// Verify state changed
const newState = await toggleSwitch.isChecked();
expect(newState).toBe(!initialState);
// Verify success message
await waitForToast(page, /actualizado|updated|guardado|saved/i);
// Verify the change persisted (refresh page)
await page.reload();
await waitForPageLoad(page);
// Check state is still changed
const persistedState = await toggleSwitch.isChecked();
expect(persistedState).toBe(newState);
// Toggle back to original state
if (persistedState !== initialState) {
await toggleSwitch.click();
await page.waitForTimeout(500);
await waitForToast(page, /actualizado|updated/i);
}
});
test('should create a new feature flag', async ({ page }) => {
// Navigate to feature flags page
await page.goto('/admin/feature-flags');
await waitForPageLoad(page);
// Click create new flag button
const createButton = page.getByRole('button', {
name: /crear|create|nuevo|new.*flag/i,
});
if (!(await createButton.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
await createButton.click();
// Wait for modal/form
const modal = await waitForModal(page);
// Fill in flag details
const nameInput = modal.locator('input[name="name"], input[name="key"]');
const descriptionInput = modal.locator(
'textarea[name="description"], input[name="description"]'
);
const flagName = `test-flag-${Date.now()}`;
await nameInput.fill(flagName);
await descriptionInput.fill('Test flag created by E2E test');
// Submit
const submitButton = modal.getByRole('button', { name: /crear|create|guardar|save/i });
await submitButton.click();
await waitForLoadingToFinish(page);
// Verify success
await waitForToast(page, /creado|created|éxito|success/i);
// Verify flag appears in list
await expect(page.locator(`text=${flagName}`)).toBeVisible({ timeout: 5000 });
// Cleanup: Delete the test flag
const deleteButton = page
.locator(`[data-flag="${flagName}"]`)
.locator('button[aria-label*="eliminar"], button[aria-label*="delete"]');
if (await deleteButton.isVisible({ timeout: 2000 })) {
await deleteButton.click();
// Confirm deletion
const confirmButton = page.getByRole('button', { name: /confirmar|confirm|eliminar|delete/i });
if (await confirmButton.isVisible({ timeout: 2000 })) {
await confirmButton.click();
await waitForToast(page, /eliminado|deleted/i);
}
}
});
test('should update feature flag targeting rules', async ({ page }) => {
// Navigate to feature flags page
await page.goto('/admin/feature-flags');
await waitForPageLoad(page);
// Click on first flag to edit
const firstFlag = page.locator('[data-testid="flag-row"], tr').filter({
hasNot: page.locator('th'),
}).first();
const editButton = firstFlag.getByRole('button', { name: /editar|edit|configurar/i });
if (!(await editButton.isVisible({ timeout: 3000 }))) {
// Try clicking the row itself
await firstFlag.click();
} else {
await editButton.click();
}
await waitForPageLoad(page);
// Look for targeting configuration
const targetingSection = page.locator(
'[data-testid="targeting-config"], .targeting, text=/targeting|segmentación/i'
);
if (!(await targetingSection.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Add a targeting rule (example: enable for specific role)
const addRuleButton = page.getByRole('button', { name: /agregar regla|add rule/i });
if (await addRuleButton.isVisible({ timeout: 2000 })) {
await addRuleButton.click();
// Fill in rule details (depends on implementation)
const attributeSelect = page.locator('select[name="attribute"]');
if (await attributeSelect.isVisible({ timeout: 2000 })) {
await attributeSelect.selectOption('role');
const valueInput = page.locator('input[name="value"]');
await valueInput.fill('teacher');
// Save rule
const saveButton = page.getByRole('button', { name: /guardar|save/i });
await saveButton.click();
await waitForLoadingToFinish(page);
await waitForToast(page, /guardado|saved/i);
}
}
});
});
test.describe('Admin Flow - Role Management', () => {
test.beforeEach(async ({ page }) => {
await loginAsAdmin(page);
await waitForPageLoad(page);
});
test('should view and manage user roles', async ({ page }) => {
// Navigate to users/roles page
const usersLink = page.getByRole('link', { name: /usuarios|users|roles/i });
if (await usersLink.isVisible({ timeout: 3000 })) {
await usersLink.click();
} else {
await page.goto('/admin/users');
}
await waitForPageLoad(page);
// Verify users list is visible
const usersList = page.locator('[data-testid="users-list"], table, [role="table"]');
await expect(usersList.first()).toBeVisible({ timeout: 10000 });
// Verify user rows exist
const userRows = page.locator('[data-testid="user-row"], tr').filter({
hasNot: page.locator('th'),
});
const count = await userRows.count();
expect(count).toBeGreaterThan(0);
});
test('should edit user permissions', async ({ page }) => {
// Navigate to users page
await page.goto('/admin/users');
await waitForPageLoad(page);
// Find a test user (not admin)
const testUserRow = page
.locator('[data-testid="user-row"], tr')
.filter({ hasText: /student|teacher/i })
.first();
if (!(await testUserRow.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Click edit button
const editButton = testUserRow.getByRole('button', { name: /editar|edit|permisos/i });
if (await editButton.isVisible({ timeout: 2000 })) {
await editButton.click();
} else {
await testUserRow.click();
}
await waitForPageLoad(page);
// Look for permissions checkboxes
const permissionsSection = page.locator(
'[data-testid="permissions"], .permissions, text=/permisos|permissions/i'
);
if (!(await permissionsSection.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Find permission checkboxes
const permissionCheckboxes = page.locator('input[type="checkbox"][name*="permission"]');
const checkboxCount = await permissionCheckboxes.count();
if (checkboxCount === 0) {
test.skip();
return;
}
// Toggle first permission
const firstCheckbox = permissionCheckboxes.first();
const initialState = await firstCheckbox.isChecked();
await firstCheckbox.click();
await page.waitForTimeout(300);
// Save changes
const saveButton = page.getByRole('button', { name: /guardar|save|actualizar/i });
await saveButton.click();
await waitForLoadingToFinish(page);
// Verify success
await waitForToast(page, /actualizado|updated|guardado/i);
// Revert changes
await firstCheckbox.click();
await saveButton.click();
await waitForLoadingToFinish(page);
});
test('should change user role', async ({ page }) => {
// Navigate to users page
await page.goto('/admin/users');
await waitForPageLoad(page);
// Find a test user
const testUserRow = page
.locator('[data-testid="user-row"], tr')
.filter({ hasText: /test.*student|test.*teacher/i })
.first();
if (!(await testUserRow.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Click edit/manage button
const editButton = testUserRow.getByRole('button', { name: /editar|edit|rol/i });
if (!(await editButton.isVisible({ timeout: 2000 }))) {
test.skip();
return;
}
await editButton.click();
await waitForPageLoad(page);
// Find role selector
const roleSelect = page.locator('select[name="role"], [data-testid="role-select"]');
if (!(await roleSelect.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Get current role
const currentRole = await roleSelect.inputValue();
// Change to different role (toggle between student/teacher)
const newRole = currentRole === 'student' ? 'teacher' : 'student';
await roleSelect.selectOption(newRole);
// Save
const saveButton = page.getByRole('button', { name: /guardar|save/i });
await saveButton.click();
await waitForLoadingToFinish(page);
// Verify success
await waitForToast(page, /actualizado|updated/i);
// Revert role back
await roleSelect.selectOption(currentRole);
await saveButton.click();
await waitForLoadingToFinish(page);
});
test('should search for users', async ({ page }) => {
// Navigate to users page
await page.goto('/admin/users');
await waitForPageLoad(page);
// Find search input
const searchInput = page.locator(
'input[type="search"], input[placeholder*="buscar"], input[placeholder*="search"]'
);
if (!(await searchInput.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Search for a user
await searchInput.fill('test');
await page.waitForTimeout(1000);
// Verify filtered results
const userRows = page.locator('[data-testid="user-row"], tr').filter({
hasNot: page.locator('th'),
});
const count = await userRows.count();
// Should show some results
expect(count).toBeGreaterThanOrEqual(0);
// Clear search
await searchInput.clear();
await page.waitForTimeout(500);
});
test('should filter users by role', async ({ page }) => {
// Navigate to users page
await page.goto('/admin/users');
await waitForPageLoad(page);
// Find role filter
const roleFilter = page.locator('select[name="role"], [data-testid="role-filter"]');
if (!(await roleFilter.isVisible({ timeout: 3000 }))) {
test.skip();
return;
}
// Filter by student role
await roleFilter.selectOption('student');
await waitForLoadingToFinish(page);
// Verify all shown users are students
const userRows = page.locator('[data-testid="user-row"], tr').filter({
hasNot: page.locator('th'),
});
const count = await userRows.count();
if (count > 0) {
// Check first row has student badge/label
const firstRow = userRows.first();
await expect(firstRow).toContainText(/student|estudiante/i);
}
// Reset filter
await roleFilter.selectOption('all');
});
});