towerops/e2e/tests/form-validation.spec.ts
Graham McIntire 0cf533c0d4
feat: add comprehensive e2e tests for workflows and validation
- Added integration tests (Preseem, Gaiia, Slack, webhooks)
- Added SNMP configuration and discovery tests
- Added form validation and error handling tests
- Added detailed alert workflow tests (lifecycle, actions, filtering, history)
- Added agent token management tests (creation, assignment, deletion)

Covers:
- Third-party integration UI
- SNMP credential hierarchy (org/site/device)
- Form validation and error states
- Alert bulk actions and filtering
- Agent token lifecycle and device assignment
2026-03-06 17:12:46 -06:00

194 lines
6.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Form Validation and Error Handling', () => {
test.describe('Site Form Validation', () => {
test('shows validation error for empty site name', async ({ page }) => {
await page.goto('/sites/new');
if ((await page.url()).includes('/sites/new')) {
// Try to submit empty form
const submitButton = page.locator('button[type="submit"]').first();
if (await submitButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await submitButton.click();
await page.waitForTimeout(500);
// Look for error message
const errorMessage = page.locator(
'.error, [role="alert"], :has-text("required"), :has-text("can\'t be blank")'
).first();
if (await errorMessage.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(errorMessage).toBeVisible();
}
}
}
});
test('shows field-level validation errors', async ({ page }) => {
await page.goto('/sites/new');
if ((await page.url()).includes('/sites/new')) {
const nameField = page.locator('input[name*="name"]').first();
if (await nameField.isVisible({ timeout: 2000 }).catch(() => false)) {
// Focus and blur to trigger validation
await nameField.focus();
await nameField.blur();
await page.waitForTimeout(300);
// Look for field error
const fieldError = page.locator('.error, [role="alert"]').first();
if (await fieldError.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(fieldError).toBeVisible();
}
}
}
});
});
test.describe('Device Form Validation', () => {
test('validates IP address format', async ({ page }) => {
await page.goto('/devices/new');
if ((await page.url()).includes('/devices/new')) {
const ipField = page.locator('input[name*="ip"], input[name*="host"]').first();
if (await ipField.isVisible({ timeout: 2000 }).catch(() => false)) {
// Enter invalid IP
await ipField.fill('invalid-ip');
await ipField.blur();
await page.waitForTimeout(300);
// Look for validation error
const error = page.locator(
':has-text("invalid"), :has-text("format"), .error'
).first();
if (await error.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(error).toBeVisible();
}
}
}
});
test('shows required field indicators', async ({ page }) => {
await page.goto('/devices/new');
if ((await page.url()).includes('/devices/new')) {
// Look for required field markers
const requiredMarker = page.locator(
'label:has-text("*"), .required, [aria-required="true"]'
).first();
if (await requiredMarker.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(requiredMarker).toBeVisible();
}
}
});
});
test.describe('Error States', () => {
test('shows error page for non-existent resources', async ({ page }) => {
await page.goto('/devices/00000000-0000-0000-0000-000000000000');
await page.waitForTimeout(500);
// Should show 404 or error message
const errorIndicator = page.locator(
':has-text("not found"), :has-text("404"), :has-text("doesn\'t exist")'
).first();
if (await errorIndicator.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(errorIndicator).toBeVisible();
}
});
test('handles network errors gracefully', async ({ page }) => {
await page.goto('/devices');
// Look for any error boundaries or fallbacks
await expect(page.locator('body')).toBeVisible();
});
});
test.describe('Form Submission States', () => {
test('shows loading state during form submission', async ({ page }) => {
await page.goto('/sites/new');
if ((await page.url()).includes('/sites/new')) {
const nameField = page.locator('input[name*="name"]').first();
const submitButton = page.locator('button[type="submit"]').first();
if (await nameField.isVisible({ timeout: 2000 }).catch(() => false)) {
await nameField.fill(`Test Site ${Date.now()}`);
if (await submitButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await submitButton.click();
// Look for loading indicator (briefly)
const loadingIndicator = page.locator(
'[disabled], .loading, :has-text("Saving"), :has-text("Creating")'
).first();
if (await loadingIndicator.isVisible({ timeout: 1000 }).catch(() => false)) {
await expect(loadingIndicator).toBeVisible();
}
}
}
}
});
test('disables submit button during submission', async ({ page }) => {
await page.goto('/sites/new');
if ((await page.url()).includes('/sites/new')) {
const submitButton = page.locator('button[type="submit"]').first();
if (await submitButton.isVisible({ timeout: 2000 }).catch(() => false)) {
const nameField = page.locator('input[name*="name"]').first();
if (await nameField.isVisible({ timeout: 2000 }).catch(() => false)) {
await nameField.fill(`Test Site ${Date.now()}`);
await submitButton.click();
// Button should be disabled during submission
const isDisabled = await submitButton.isDisabled().catch(() => false);
if (isDisabled) {
expect(isDisabled).toBe(true);
}
}
}
}
});
});
test.describe('Success Messages', () => {
test('shows success message after creating resource', async ({ page }) => {
await page.goto('/sites/new');
if ((await page.url()).includes('/sites/new')) {
const nameField = page.locator('input[name*="name"]').first();
const submitButton = page.locator('button[type="submit"]').first();
if (await nameField.isVisible({ timeout: 2000 }).catch(() => false)) {
await nameField.fill(`E2E Test Site ${Date.now()}`);
if (await submitButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await submitButton.click();
await page.waitForTimeout(1000);
// Look for success message
const successMessage = page.locator(
':has-text("created"), :has-text("success"), .success, [role="status"]'
).first();
if (await successMessage.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(successMessage).toBeVisible();
}
}
}
}
});
});
});