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
This commit is contained in:
Graham McIntire 2026-03-06 17:12:46 -06:00
parent 7f7e289325
commit 0cf533c0d4
No known key found for this signature in database
5 changed files with 1056 additions and 0 deletions

View file

@ -0,0 +1,258 @@
import { test, expect } from '@playwright/test';
test.describe('Agent Token Management', () => {
test.describe('Token List', () => {
test('can view agent tokens from agents page', async ({ page }) => {
await page.goto('/agents');
await expect(page).toHaveURL(/\/agents/);
await expect(page.locator('body')).toBeVisible();
});
test('shows agent token details', async ({ page }) => {
await page.goto('/agents');
// Look for token information
const tokenInfo = page.locator(
':has-text("Token"), [data-token], .token'
).first();
if (await tokenInfo.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(tokenInfo).toBeVisible();
}
});
test('shows agent status indicators', async ({ page }) => {
await page.goto('/agents');
// Look for online/offline status
const statusIndicator = page.locator(
':has-text("Online"), :has-text("Offline"), .status, [data-status]'
).first();
if (await statusIndicator.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(statusIndicator).toBeVisible();
}
});
test('shows last seen timestamp', async ({ page }) => {
await page.goto('/agents');
// Look for last seen time
const lastSeen = page.locator(
':has-text("Last seen"), :has-text("ago"), time'
).first();
if (await lastSeen.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(lastSeen).toBeVisible();
}
});
});
test.describe('Token Creation', () => {
test('can access new agent token page', async ({ page }) => {
await page.goto('/agents');
// Look for create new agent button
const newButton = page.locator(
'button:has-text("New"), button:has-text("Add"), a:has-text("New Agent")'
).first();
if (await newButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await newButton.click();
await page.waitForTimeout(500);
}
});
test('shows token name field', async ({ page }) => {
await page.goto('/agents');
const newButton = page.locator(
'button:has-text("New"), a[href*="/agents/new"]'
).first();
if (await newButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await newButton.click();
await page.waitForTimeout(500);
// Look for name field
const nameField = page.locator('input[name*="name"]').first();
if (await nameField.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(nameField).toBeVisible();
}
}
});
test('shows token after creation', async ({ page }) => {
await page.goto('/agents');
// After creating a token, should show the token value
// This is a read-only test checking the UI exists
const tokenDisplay = page.locator(
'code, pre, [data-token-value], input[readonly]'
).first();
if (await tokenDisplay.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(tokenDisplay).toBeVisible();
}
});
});
test.describe('Token Assignment', () => {
test('can view device assignments', async ({ page }) => {
await page.goto('/agents');
// Navigate to first agent
const firstAgent = page.locator('a[href*="/agents/"]').first();
if (await firstAgent.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAgent.click();
await page.waitForTimeout(500);
// Look for assigned devices
const devicesSection = page.locator(
':has-text("Devices"), :has-text("Assigned"), table'
).first();
if (await devicesSection.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(devicesSection).toBeVisible();
}
}
});
test('shows device count for agent', async ({ page }) => {
await page.goto('/agents');
// Look for device count
const deviceCount = page.locator(
':has-text("device"), [data-count], .count'
).first();
if (await deviceCount.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(deviceCount).toBeVisible();
}
});
test('can assign devices to agent', async ({ page }) => {
await page.goto('/agents');
const firstAgent = page.locator('a[href*="/agents/"]').first();
if (await firstAgent.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAgent.click();
await page.waitForTimeout(500);
// Look for assign button
const assignButton = page.locator(
'button:has-text("Assign"), button:has-text("Add Device")'
).first();
if (await assignButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(assignButton).toBeVisible();
}
}
});
test('can unassign devices from agent', async ({ page }) => {
await page.goto('/agents');
const firstAgent = page.locator('a[href*="/agents/"]').first();
if (await firstAgent.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAgent.click();
await page.waitForTimeout(500);
// Look for remove/unassign button
const removeButton = page.locator(
'button:has-text("Remove"), button:has-text("Unassign")'
).first();
if (await removeButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(removeButton).toBeVisible();
}
}
});
});
test.describe('Token Deletion', () => {
test('can access delete token option', async ({ page }) => {
await page.goto('/agents');
const firstAgent = page.locator('a[href*="/agents/"]').first();
if (await firstAgent.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAgent.click();
await page.waitForTimeout(500);
// Look for delete button
const deleteButton = page.locator(
'button:has-text("Delete"), button:has-text("Remove")'
).first();
if (await deleteButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(deleteButton).toBeVisible();
}
}
});
test('shows confirmation before deleting token', async ({ page }) => {
await page.goto('/agents');
const firstAgent = page.locator('a[href*="/agents/"]').first();
if (await firstAgent.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAgent.click();
await page.waitForTimeout(500);
const deleteButton = page.locator('button:has-text("Delete")').first();
if (await deleteButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await deleteButton.click();
await page.waitForTimeout(300);
// Look for confirmation dialog
const confirmation = page.locator(
':has-text("confirm"), :has-text("sure"), [role="dialog"]'
).first();
if (await confirmation.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(confirmation).toBeVisible();
}
}
}
});
});
test.describe('Default Agent Token', () => {
test('can set organization default agent', async ({ page }) => {
await page.goto('/settings');
if ((await page.url()).includes('sudo-verify')) {
return;
}
// Look for default agent setting
const defaultAgentSetting = page.locator(
':has-text("Default Agent"), select[name*="agent"]'
).first();
if (await defaultAgentSetting.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(defaultAgentSetting).toBeVisible();
}
});
test('shows default agent indicator', async ({ page }) => {
await page.goto('/agents');
// Look for default badge or indicator
const defaultBadge = page.locator(
':has-text("Default"), .badge, [data-default]'
).first();
if (await defaultBadge.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(defaultBadge).toBeVisible();
}
});
});
});

View file

@ -0,0 +1,252 @@
import { test, expect } from '@playwright/test';
test.describe('Alert Workflows', () => {
test.describe('Alert Lifecycle', () => {
test('can view alert details', async ({ page }) => {
await page.goto('/alerts');
// Find first alert
const firstAlert = page.locator('[data-alert], tr:not(:has(th)), a[href*="/alerts/"]').first();
if (await firstAlert.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAlert.click();
await page.waitForTimeout(500);
// Should show alert details
const alertDetails = page.locator(
':has-text("Device"), :has-text("Time"), [data-alert-detail]'
).first();
if (await alertDetails.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(alertDetails).toBeVisible();
}
}
});
test('shows alert severity', async ({ page }) => {
await page.goto('/alerts');
// Look for severity indicators
const severityBadge = page.locator(
'.badge, :has-text("Critical"), :has-text("Warning"), :has-text("Info"), [data-severity]'
).first();
if (await severityBadge.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(severityBadge).toBeVisible();
}
});
test('shows alert timestamp', async ({ page }) => {
await page.goto('/alerts');
// Look for timestamp
const timestamp = page.locator(
'time, :has-text("ago"), [data-timestamp]'
).first();
if (await timestamp.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(timestamp).toBeVisible();
}
});
test('shows device information in alert', async ({ page }) => {
await page.goto('/alerts');
// Look for device name or link
const deviceLink = page.locator(
'a[href*="/devices/"]'
).first();
if (await deviceLink.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(deviceLink).toBeVisible();
}
});
});
test.describe('Alert Actions', () => {
test('can acknowledge multiple alerts', async ({ page }) => {
await page.goto('/alerts');
// Look for bulk actions
const bulkAckButton = page.locator(
'button:has-text("Acknowledge Selected"), button:has-text("Bulk")'
).first();
if (await bulkAckButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(bulkAckButton).toBeVisible();
}
});
test('can select alerts for bulk actions', async ({ page }) => {
await page.goto('/alerts');
// Look for checkboxes
const checkbox = page.locator('input[type="checkbox"]').first();
if (await checkbox.isVisible({ timeout: 2000 }).catch(() => false)) {
await checkbox.click();
await page.waitForTimeout(300);
}
});
test('shows acknowledge confirmation', async ({ page }) => {
await page.goto('/alerts');
const ackButton = page.locator('button:has-text("Acknowledge")').first();
if (await ackButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await ackButton.click();
await page.waitForTimeout(500);
// Look for confirmation or success message
const confirmation = page.locator(
':has-text("acknowledged"), .success, [role="status"]'
).first();
if (await confirmation.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(confirmation).toBeVisible();
}
}
});
test('can add notes to alert', async ({ page }) => {
await page.goto('/alerts');
const firstAlert = page.locator('[data-alert], tr:not(:has(th))').first();
if (await firstAlert.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstAlert.click();
await page.waitForTimeout(500);
// Look for notes section
const notesSection = page.locator(
'textarea, input[name*="note"], :has-text("Note"), :has-text("Comment")'
).first();
if (await notesSection.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(notesSection).toBeVisible();
}
}
});
});
test.describe('Alert Filtering', () => {
test('can filter by date range', async ({ page }) => {
await page.goto('/alerts');
const dateFilter = page.locator(
'input[type="date"], button:has-text("Date"), [data-date-filter]'
).first();
if (await dateFilter.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(dateFilter).toBeVisible();
}
});
test('can filter by device', async ({ page }) => {
await page.goto('/alerts');
const deviceFilter = page.locator(
'select[name*="device"], input[placeholder*="Device"]'
).first();
if (await deviceFilter.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(deviceFilter).toBeVisible();
}
});
test('can filter by alert type', async ({ page }) => {
await page.goto('/alerts');
const typeFilter = page.locator(
'select[name*="type"], button:has-text("Type")'
).first();
if (await typeFilter.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(typeFilter).toBeVisible();
}
});
test('shows active filter badges', async ({ page }) => {
await page.goto('/alerts?status=active');
// Look for filter badges showing active filters
const filterBadge = page.locator(
'.badge, .tag, :has-text("Active"), [data-filter-badge]'
).first();
if (await filterBadge.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(filterBadge).toBeVisible();
}
});
test('can clear all filters', async ({ page }) => {
await page.goto('/alerts?status=active');
const clearButton = page.locator(
'button:has-text("Clear"), button:has-text("Reset")'
).first();
if (await clearButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await clearButton.click();
await page.waitForTimeout(500);
}
});
});
test.describe('Alert Notifications', () => {
test('shows alert count badge', async ({ page }) => {
await page.goto('/dashboard');
// Look for alert count in navigation
const alertBadge = page.locator(
'[data-alert-count], .badge, a[href*="/alerts"] .count'
).first();
if (await alertBadge.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(alertBadge).toBeVisible();
}
});
test('can navigate to alerts from notification', async ({ page }) => {
await page.goto('/dashboard');
const alertLink = page.locator('a[href*="/alerts"]').first();
if (await alertLink.isVisible({ timeout: 2000 }).catch(() => false)) {
await alertLink.click();
await page.waitForTimeout(500);
await expect(page).toHaveURL(/\/alerts/);
}
});
});
test.describe('Alert History', () => {
test('shows resolved alerts', async ({ page }) => {
await page.goto('/alerts');
// Switch to resolved tab/filter
const resolvedTab = page.locator(
'button:has-text("Resolved"), [role="tab"]:has-text("Resolved")'
).first();
if (await resolvedTab.isVisible({ timeout: 2000 }).catch(() => false)) {
await resolvedTab.click();
await page.waitForTimeout(500);
}
});
test('shows alert resolution time', async ({ page }) => {
await page.goto('/alerts');
// Look for resolution timestamp
const resolvedTime = page.locator(
':has-text("Resolved"), time'
).first();
if (await resolvedTime.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(resolvedTime).toBeVisible();
}
});
});
});

View file

@ -0,0 +1,194 @@
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();
}
}
}
}
});
});
});

View file

@ -0,0 +1,167 @@
import { test, expect } from '@playwright/test';
test.describe('Third-Party Integrations', () => {
test.describe('Preseem Integration', () => {
test('can access Preseem devices page', async ({ page }) => {
await page.goto('/settings/integrations/preseem/devices');
// Skip if redirected to sudo verification or login
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
await expect(page).toHaveURL(/\/settings\/integrations\/preseem\/devices/);
await expect(page.locator('body')).toBeVisible();
});
test('shows Preseem device mapping', async ({ page }) => {
await page.goto('/settings/integrations/preseem/devices');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
// Look for device mapping interface
const mappingInterface = page.locator(
':has-text("Preseem"), table, [data-device]'
).first();
if (await mappingInterface.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(mappingInterface).toBeVisible();
}
});
test('can access Preseem insights page', async ({ page }) => {
await page.goto('/settings/integrations/preseem/insights');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
await expect(page).toHaveURL(/\/settings\/integrations\/preseem\/insights/);
await expect(page.locator('body')).toBeVisible();
});
test('shows Preseem insights data', async ({ page }) => {
await page.goto('/settings/integrations/preseem/insights');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
// Look for insights data
const insightsData = page.locator(
'table, [data-insight], :has-text("Preseem")'
).first();
if (await insightsData.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(insightsData).toBeVisible();
}
});
});
test.describe('Gaiia Integration', () => {
test('can access Gaiia mapping page', async ({ page }) => {
await page.goto('/settings/integrations/gaiia/mapping');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
await expect(page).toHaveURL(/\/settings\/integrations\/gaiia\/mapping/);
await expect(page.locator('body')).toBeVisible();
});
test('shows Gaiia device mapping interface', async ({ page }) => {
await page.goto('/settings/integrations/gaiia/mapping');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
// Look for mapping controls
const mappingControls = page.locator(
'select, button:has-text("Map"), table'
).first();
if (await mappingControls.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(mappingControls).toBeVisible();
}
});
test('can access Gaiia reconciliation page', async ({ page }) => {
await page.goto('/settings/integrations/gaiia/reconciliation');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
await expect(page).toHaveURL(/\/settings\/integrations\/gaiia\/reconciliation/);
await expect(page.locator('body')).toBeVisible();
});
test('shows reconciliation status', async ({ page }) => {
await page.goto('/settings/integrations/gaiia/reconciliation');
if ((await page.url()).includes('sudo-verify') || (await page.url()).includes('log-in')) {
return;
}
// Look for reconciliation status
const status = page.locator(
':has-text("synced"), :has-text("pending"), table'
).first();
if (await status.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(status).toBeVisible();
}
});
});
test.describe('Integration Settings', () => {
test('can view all integrations', async ({ page }) => {
await page.goto('/settings/integrations');
if ((await page.url()).includes('sudo-verify')) {
return;
}
await expect(page).toHaveURL(/\/settings\/integrations/);
await expect(page.locator('body')).toBeVisible();
});
test('shows available integrations', async ({ page }) => {
await page.goto('/settings/integrations');
if ((await page.url()).includes('sudo-verify')) {
return;
}
// Look for integration cards or list
const integrationCard = page.locator(
':has-text("Slack"), :has-text("Webhook"), :has-text("Preseem"), :has-text("Gaiia")'
).first();
if (await integrationCard.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(integrationCard).toBeVisible();
}
});
test('can test integration connection', async ({ page }) => {
await page.goto('/settings/integrations');
if ((await page.url()).includes('sudo-verify')) {
return;
}
// Look for test connection button
const testButton = page.locator(
'button:has-text("Test"), button:has-text("Test Connection")'
).first();
if (await testButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(testButton).toBeVisible();
}
});
});
});

View file

@ -0,0 +1,185 @@
import { test, expect } from '@playwright/test';
test.describe('SNMP Configuration', () => {
test.describe('Device SNMP Settings', () => {
test('can access SNMP settings from device page', async ({ page }) => {
await page.goto('/devices');
// Navigate to first device
const firstDevice = page.locator('a[href*="/devices/"]:not([href*="/devices/new"])').first();
if (await firstDevice.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstDevice.click();
await page.waitForTimeout(500);
// Look for edit or settings button
const editButton = page.locator(
'a[href*="/edit"], button:has-text("Edit")'
).first();
if (await editButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await editButton.click();
await page.waitForTimeout(500);
}
}
});
test('shows SNMP credential fields', async ({ page }) => {
await page.goto('/devices');
const firstDevice = page.locator('a[href*="/devices/"]:not([href*="/devices/new"])').first();
if (await firstDevice.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstDevice.click();
await page.waitForTimeout(500);
const editButton = page.locator('a[href*="/edit"]').first();
if (await editButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await editButton.click();
await page.waitForTimeout(500);
// Look for SNMP fields
const snmpFields = page.locator(
'input[name*="snmp"], select[name*="version"], :has-text("Community"), :has-text("SNMP")'
).first();
if (await snmpFields.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(snmpFields).toBeVisible();
}
}
}
});
test('shows SNMP version selector', async ({ page }) => {
await page.goto('/devices');
const firstDevice = page.locator('a[href*="/devices/"]:not([href*="/devices/new"])').first();
if (await firstDevice.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstDevice.click();
await page.waitForTimeout(500);
const editButton = page.locator('a[href*="/edit"]').first();
if (await editButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await editButton.click();
await page.waitForTimeout(500);
// Look for SNMP version selector
const versionSelect = page.locator(
'select[name*="version"], :has-text("v2c"), :has-text("v3")'
).first();
if (await versionSelect.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(versionSelect).toBeVisible();
}
}
}
});
});
test.describe('SNMP Discovery', () => {
test('can access discovery page', async ({ page }) => {
await page.goto('/devices/new');
// Check if we can access the new device page
if ((await page.url()).includes('/devices/new')) {
await expect(page.locator('body')).toBeVisible();
}
});
test('shows discovery form fields', async ({ page }) => {
await page.goto('/devices/new');
if ((await page.url()).includes('/devices/new')) {
// Look for IP address or hostname field
const ipField = page.locator(
'input[name*="ip"], input[name*="host"], input[placeholder*="IP"]'
).first();
if (await ipField.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(ipField).toBeVisible();
}
}
});
test('shows SNMP community field for discovery', async ({ page }) => {
await page.goto('/devices/new');
if ((await page.url()).includes('/devices/new')) {
// Look for SNMP community field
const communityField = page.locator(
'input[name*="community"], :has-text("Community")'
).first();
if (await communityField.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(communityField).toBeVisible();
}
}
});
test('shows discover button', async ({ page }) => {
await page.goto('/devices/new');
if ((await page.url()).includes('/devices/new')) {
// Look for discover button
const discoverButton = page.locator(
'button:has-text("Discover"), button:has-text("Add"), button[type="submit"]'
).first();
if (await discoverButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(discoverButton).toBeVisible();
}
}
});
});
test.describe('SNMP Credentials Hierarchy', () => {
test('can configure site-level SNMP credentials', async ({ page }) => {
await page.goto('/sites');
// Navigate to first site
const firstSite = page.locator('a[href*="/sites/"]:not([href*="/sites/new"])').first();
if (await firstSite.isVisible({ timeout: 2000 }).catch(() => false)) {
await firstSite.click();
await page.waitForTimeout(500);
// Look for edit button
const editButton = page.locator('a[href*="/edit"]').first();
if (await editButton.isVisible({ timeout: 2000 }).catch(() => false)) {
await editButton.click();
await page.waitForTimeout(500);
// Look for SNMP credential fields
const snmpSection = page.locator(
':has-text("SNMP"), input[name*="community"]'
).first();
if (await snmpSection.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(snmpSection).toBeVisible();
}
}
}
});
test('can configure organization-level SNMP credentials', async ({ page }) => {
await page.goto('/settings');
if ((await page.url()).includes('sudo-verify')) {
return;
}
// Look for SNMP settings section
const snmpSection = page.locator(
':has-text("SNMP"), :has-text("Default"), input[name*="community"]'
).first();
if (await snmpSection.isVisible({ timeout: 2000 }).catch(() => false)) {
await expect(snmpSection).toBeVisible();
}
});
});
});