feat: create admin settings workflow e2e tests
This commit is contained in:
332
frontend/e2e/workflows/4-admin-settings.spec.ts
Normal file
332
frontend/e2e/workflows/4-admin-settings.spec.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import * as auth from '../fixtures/auth';
|
||||
import * as assertions from '../utils/assertions';
|
||||
import * as helpers from '../utils/helpers';
|
||||
import { LOCAL_USERS, TEST_CATEGORIES } from '../fixtures/test-data';
|
||||
|
||||
test.describe('Admin Settings Workflow', () => {
|
||||
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to app and login as admin
|
||||
await page.goto(BASE_URL);
|
||||
await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL);
|
||||
await assertions.assertUserAuthenticated(page, BASE_URL);
|
||||
|
||||
// Open admin panel
|
||||
const adminButton = page.locator('[data-testid="admin-button"]');
|
||||
await adminButton.click();
|
||||
|
||||
// Wait for admin overlay to appear
|
||||
await assertions.assertAdminDashboardVisible(page);
|
||||
});
|
||||
|
||||
test('should display admin dashboard with all tabs', async ({ page }) => {
|
||||
await assertions.assertAdminDashboardVisible(page);
|
||||
|
||||
// Verify all tabs are present
|
||||
const identityTab = page.locator('[data-testid="admin-tab-identity"]');
|
||||
const databaseTab = page.locator('[data-testid="admin-tab-database"]');
|
||||
const ldapTab = page.locator('[data-testid="admin-tab-ldap"]');
|
||||
const aiTab = page.locator('[data-testid="admin-tab-ai"]');
|
||||
const categoriesTab = page.locator('[data-testid="admin-tab-categories"]');
|
||||
|
||||
await expect(identityTab).toBeVisible();
|
||||
await expect(databaseTab).toBeVisible();
|
||||
await expect(ldapTab).toBeVisible();
|
||||
await expect(aiTab).toBeVisible();
|
||||
await expect(categoriesTab).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display users in identity tab', async ({ page }) => {
|
||||
const identityTab = page.locator('[data-testid="admin-tab-identity"]');
|
||||
await identityTab.click();
|
||||
|
||||
// User list should be displayed
|
||||
const userList = page.locator('[data-testid="user-list"]');
|
||||
await expect(userList).toBeVisible();
|
||||
|
||||
// Should have at least one user (admin)
|
||||
const userItems = page.locator('[data-testid="user-list-item"]');
|
||||
const count = await userItems.count();
|
||||
expect(count).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test('should allow creating new user', async ({ page }) => {
|
||||
const identityTab = page.locator('[data-testid="admin-tab-identity"]');
|
||||
await identityTab.click();
|
||||
|
||||
// Click add user button
|
||||
const addUserButton = page.locator('[data-testid="add-user-button"]');
|
||||
await addUserButton.click();
|
||||
|
||||
// Modal should appear
|
||||
const modal = page.locator('[data-testid="create-user-modal"]');
|
||||
await expect(modal).toBeVisible();
|
||||
|
||||
// Fill form
|
||||
await page.fill('[data-testid="new-user-name"]', 'TestUser123');
|
||||
await page.fill('[data-testid="new-user-password"]', 'Password123!');
|
||||
|
||||
// Submit
|
||||
const submitButton = page.locator('[data-testid="create-user-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Should show success
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should allow deleting user with confirmation', async ({ page }) => {
|
||||
const identityTab = page.locator('[data-testid="admin-tab-identity"]');
|
||||
await identityTab.click();
|
||||
|
||||
// Find a non-admin user to delete
|
||||
const deleteButtons = page.locator('[data-testid="delete-user-button"]');
|
||||
if (await deleteButtons.count() > 0) {
|
||||
// Click delete on first user
|
||||
await deleteButtons.first().click();
|
||||
|
||||
// Confirmation should appear
|
||||
const confirmDialog = page.locator('[data-testid="confirmation-modal"]');
|
||||
await expect(confirmDialog).toBeVisible();
|
||||
|
||||
// Confirm deletion
|
||||
const confirmButton = page.locator('[data-testid="confirm-action"]');
|
||||
await confirmButton.click();
|
||||
|
||||
// Should show success
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should display database info in database tab', async ({ page }) => {
|
||||
const databaseTab = page.locator('[data-testid="admin-tab-database"]');
|
||||
await databaseTab.click();
|
||||
|
||||
// Database info should be displayed
|
||||
const dbInfo = page.locator('[data-testid="database-info"]');
|
||||
await expect(dbInfo).toBeVisible();
|
||||
|
||||
// Should show stats
|
||||
const itemCount = page.locator('[data-testid="item-count"]');
|
||||
if (await itemCount.isVisible()) {
|
||||
const count = await itemCount.textContent();
|
||||
expect(count).toMatch(/\d+/);
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow backup creation', async ({ page }) => {
|
||||
const databaseTab = page.locator('[data-testid="admin-tab-database"]');
|
||||
await databaseTab.click();
|
||||
|
||||
// Backup button should be present
|
||||
const backupButton = page.locator('[data-testid="backup-button"]');
|
||||
if (await backupButton.isVisible()) {
|
||||
await backupButton.click();
|
||||
|
||||
// Should show progress or success
|
||||
const progressOrSuccess = page.locator(
|
||||
'[data-testid="backup-progress"], [data-testid="backup-success"]'
|
||||
);
|
||||
await expect(progressOrSuccess).toBeVisible({ timeout: 10000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should display LDAP configuration in ldap tab', async ({ page }) => {
|
||||
const ldapTab = page.locator('[data-testid="admin-tab-ldap"]');
|
||||
await ldapTab.click();
|
||||
|
||||
// LDAP config should be displayed
|
||||
const ldapConfig = page.locator('[data-testid="ldap-config"]');
|
||||
await expect(ldapConfig).toBeVisible();
|
||||
});
|
||||
|
||||
test('should allow enabling/disabling LDAP', async ({ page }) => {
|
||||
const ldapTab = page.locator('[data-testid="admin-tab-ldap"]');
|
||||
await ldapTab.click();
|
||||
|
||||
// Toggle LDAP
|
||||
const ldapToggle = page.locator('[data-testid="ldap-enabled-toggle"]');
|
||||
if (await ldapToggle.isVisible()) {
|
||||
const initialState = await ldapToggle.isChecked();
|
||||
|
||||
// Click toggle
|
||||
await ldapToggle.click();
|
||||
|
||||
// Verify state changed
|
||||
const newState = await ldapToggle.isChecked();
|
||||
expect(newState).not.toBe(initialState);
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow saving LDAP settings', async ({ page }) => {
|
||||
const ldapTab = page.locator('[data-testid="admin-tab-ldap"]');
|
||||
await ldapTab.click();
|
||||
|
||||
// Modify LDAP setting
|
||||
const ldapServerInput = page.locator('[data-testid="ldap-server-input"]');
|
||||
if (await ldapServerInput.isVisible()) {
|
||||
const currentValue = await ldapServerInput.inputValue();
|
||||
await ldapServerInput.fill('ldap.example.com');
|
||||
|
||||
// Save
|
||||
const saveButton = page.locator('[data-testid="save-settings-button"]');
|
||||
await saveButton.click();
|
||||
|
||||
// Should show success
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should display AI configuration in ai tab', async ({ page }) => {
|
||||
const aiTab = page.locator('[data-testid="admin-tab-ai"]');
|
||||
await aiTab.click();
|
||||
|
||||
// AI config should be displayed
|
||||
const aiConfig = page.locator('[data-testid="ai-config"]');
|
||||
await expect(aiConfig).toBeVisible();
|
||||
|
||||
// Provider selection should be visible
|
||||
const providerSelect = page.locator('[data-testid="ai-provider-select"]');
|
||||
await expect(providerSelect).toBeVisible();
|
||||
});
|
||||
|
||||
test('should allow selecting AI provider', async ({ page }) => {
|
||||
const aiTab = page.locator('[data-testid="admin-tab-ai"]');
|
||||
await aiTab.click();
|
||||
|
||||
const providerSelect = page.locator('[data-testid="ai-provider-select"]');
|
||||
const options = page.locator('[data-testid="provider-option"]');
|
||||
|
||||
if (await options.count() > 1) {
|
||||
// Select second option
|
||||
await options.nth(1).click();
|
||||
|
||||
// Should trigger save
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
if (await successToast.isVisible({ timeout: 1000 }).catch(() => false)) {
|
||||
await expect(successToast).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow entering API keys', async ({ page }) => {
|
||||
const aiTab = page.locator('[data-testid="admin-tab-ai"]');
|
||||
await aiTab.click();
|
||||
|
||||
// API key input should be present
|
||||
const apiKeyInput = page.locator('[data-testid="ai-api-key-input"]');
|
||||
if (await apiKeyInput.isVisible()) {
|
||||
await apiKeyInput.fill('test-api-key-12345');
|
||||
|
||||
// Key should be masked
|
||||
const inputType = await apiKeyInput.getAttribute('type');
|
||||
expect(inputType).toBe('password');
|
||||
|
||||
// Save
|
||||
const saveButton = page.locator('[data-testid="save-settings-button"]');
|
||||
if (await saveButton.isVisible()) {
|
||||
await saveButton.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('should display categories list in categories tab', async ({ page }) => {
|
||||
const categoriesTab = page.locator('[data-testid="admin-tab-categories"]');
|
||||
await categoriesTab.click();
|
||||
|
||||
// Categories list should be displayed
|
||||
const categoryList = page.locator('[data-testid="category-list"]');
|
||||
await expect(categoryList).toBeVisible();
|
||||
|
||||
// Should have category items
|
||||
const categoryItems = page.locator('[data-testid="category-item"]');
|
||||
const count = await categoryItems.count();
|
||||
expect(count).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test('should allow adding new category', async ({ page }) => {
|
||||
const categoriesTab = page.locator('[data-testid="admin-tab-categories"]');
|
||||
await categoriesTab.click();
|
||||
|
||||
// Add category button
|
||||
const addButton = page.locator('[data-testid="add-category-button"]');
|
||||
if (await addButton.isVisible()) {
|
||||
await addButton.click();
|
||||
|
||||
// Form should appear
|
||||
const categoryForm = page.locator('[data-testid="category-form"]');
|
||||
await expect(categoryForm).toBeVisible();
|
||||
|
||||
// Fill form
|
||||
const nameInput = page.locator('[data-testid="category-name-input"]');
|
||||
await nameInput.fill('New Category');
|
||||
|
||||
// Submit
|
||||
const submitButton = page.locator('[data-testid="add-category-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Should show success
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow deleting category', async ({ page }) => {
|
||||
const categoriesTab = page.locator('[data-testid="admin-tab-categories"]');
|
||||
await categoriesTab.click();
|
||||
|
||||
// Delete button
|
||||
const deleteButtons = page.locator('[data-testid="delete-category-button"]');
|
||||
if (await deleteButtons.count() > 0) {
|
||||
await deleteButtons.first().click();
|
||||
|
||||
// Confirmation should appear
|
||||
const confirmDialog = page.locator('[data-testid="confirmation-modal"]');
|
||||
await expect(confirmDialog).toBeVisible();
|
||||
|
||||
// Confirm
|
||||
const confirmButton = page.locator('[data-testid="confirm-action"]');
|
||||
await confirmButton.click();
|
||||
|
||||
// Should show success
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should close admin dashboard', async ({ page }) => {
|
||||
// Close button should be present
|
||||
const closeButton = page.locator('[data-testid="close-admin-overlay"]');
|
||||
if (await closeButton.isVisible()) {
|
||||
await closeButton.click();
|
||||
|
||||
// Admin overlay should close
|
||||
const adminOverlay = page.locator('[data-testid="admin-overlay"]');
|
||||
await expect(adminOverlay).not.toBeVisible({ timeout: 3000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should prevent non-admin users from accessing admin panel', async ({ page }) => {
|
||||
// Login as regular user first
|
||||
await page.goto(BASE_URL);
|
||||
await auth.logout(page, BASE_URL);
|
||||
|
||||
// Login with regular user (if available)
|
||||
await page.goto(BASE_URL);
|
||||
// Try to access admin endpoint directly
|
||||
await page.goto(`${BASE_URL}/admin`);
|
||||
|
||||
// Should be redirected or show error
|
||||
const adminOverlay = page.locator('[data-testid="admin-overlay"]');
|
||||
const errorMessage = page.locator('[data-testid="unauthorized-error"]');
|
||||
|
||||
const isBlocked = !(await adminOverlay.isVisible().catch(() => false)) ||
|
||||
(await errorMessage.isVisible().catch(() => false));
|
||||
|
||||
expect(isBlocked).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user