fix: refactor domain and DNS management tests for improved structure and clarity
This commit is contained in:
@@ -1,262 +1,210 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { test, expect, loginUser } from '../fixtures/auth-fixtures';
|
||||
import {
|
||||
clickAndWaitForResponse,
|
||||
waitForAPIResponse,
|
||||
waitForLoadingComplete,
|
||||
waitForModal,
|
||||
waitForResourceInUI,
|
||||
} from '../utils/wait-helpers';
|
||||
|
||||
/**
|
||||
* Phase 4 UAT: Domain & DNS Management
|
||||
* Domain & DNS Management Workflow
|
||||
*
|
||||
* Purpose: Validate domain and DNS provider management
|
||||
* Scenarios: Add domain, configure DNS, SSL cert management
|
||||
* Success: Domains created and certificates managed
|
||||
*/
|
||||
|
||||
test.describe('UAT-005: Domain & DNS Management', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.waitForSelector('[data-testid="dashboard-container"], [role="main"]', { timeout: 5000 });
|
||||
const DOMAIN_API_PATTERN = /\/api\/v1\/domains/;
|
||||
const DNS_PROVIDERS_API_PATTERN = /\/api\/v1\/dns-providers/;
|
||||
|
||||
function generateDomainName(seed: string): string {
|
||||
const timestamp = Date.now().toString(36);
|
||||
return `${seed}-${timestamp}.example.com`;
|
||||
}
|
||||
|
||||
async function navigateToDomains(page: import('@playwright/test').Page): Promise<void> {
|
||||
const domainsResponse = waitForAPIResponse(page, DOMAIN_API_PATTERN);
|
||||
await page.goto('/domains');
|
||||
await domainsResponse;
|
||||
await waitForLoadingComplete(page);
|
||||
}
|
||||
|
||||
async function navigateToDnsProviders(page: import('@playwright/test').Page): Promise<void> {
|
||||
const providersResponse = waitForAPIResponse(page, DNS_PROVIDERS_API_PATTERN);
|
||||
await page.goto('/dns/providers');
|
||||
await providersResponse;
|
||||
await waitForLoadingComplete(page);
|
||||
}
|
||||
|
||||
test.describe('Domain & DNS Management', () => {
|
||||
test.beforeEach(async ({ page, adminUser }) => {
|
||||
await loginUser(page, adminUser);
|
||||
await waitForLoadingComplete(page, { timeout: 15000 });
|
||||
await expect(page.getByRole('main')).toBeVisible({ timeout: 15000 });
|
||||
});
|
||||
|
||||
// UAT-401: Add domain
|
||||
test('Add domain to system', async ({ page }) => {
|
||||
// Add domain
|
||||
test('Domain - add via UI and verify in list', async ({ page }) => {
|
||||
const domainName = generateDomainName('ui-domain');
|
||||
let createdId: string | undefined;
|
||||
|
||||
await test.step('Navigate to domains page', async () => {
|
||||
const domainsLink = page.getByRole('link', { name: /domain|dns/i });
|
||||
await domainsLink.click();
|
||||
await page.waitForSelector('[data-testid="domains-list"], [class*="domain"]', { timeout: 5000 });
|
||||
await navigateToDomains(page);
|
||||
});
|
||||
|
||||
await test.step('Click add domain button', async () => {
|
||||
const addButton = page.getByRole('button', { name: /add|create|new/i }).first();
|
||||
await test.step('Fill and submit domain form', async () => {
|
||||
const domainInput = page.getByRole('textbox').first();
|
||||
await expect(domainInput).toBeVisible();
|
||||
await domainInput.fill(domainName);
|
||||
|
||||
const addButton = page.getByRole('button', { name: /add domain/i }).first();
|
||||
const response = await clickAndWaitForResponse(page, addButton, DOMAIN_API_PATTERN, { status: 201 });
|
||||
const payload = await response.json();
|
||||
createdId = payload.uuid || payload.id;
|
||||
});
|
||||
|
||||
await test.step('Verify domain card is visible', async () => {
|
||||
await waitForResourceInUI(page, domainName);
|
||||
await expect(page.getByRole('heading', { name: domainName })).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Clean up domain via API', async () => {
|
||||
if (createdId) {
|
||||
await page.request.delete(`/api/v1/domains/${createdId}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// View DNS records
|
||||
test('Domain - delete via UI with confirmation dialog', async ({ page }) => {
|
||||
const domainName = generateDomainName('delete-domain');
|
||||
const createResponse = await page.request.post('/api/v1/domains', {
|
||||
data: { name: domainName },
|
||||
});
|
||||
const created = await createResponse.json();
|
||||
const domainId = created.uuid || created.id;
|
||||
|
||||
await test.step('Navigate to domains page', async () => {
|
||||
await navigateToDomains(page);
|
||||
});
|
||||
|
||||
await test.step('Confirm domain card is visible', async () => {
|
||||
await waitForResourceInUI(page, domainName);
|
||||
await expect(page.getByRole('heading', { name: domainName })).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Delete domain from card', async () => {
|
||||
const domainCard = page.locator('div').filter({
|
||||
has: page.getByRole('heading', { name: domainName }),
|
||||
}).first();
|
||||
await expect(domainCard).toBeVisible();
|
||||
|
||||
const deleteButton = domainCard.getByRole('button', { name: /delete/i }).first();
|
||||
await expect(deleteButton).toBeVisible();
|
||||
|
||||
page.once('dialog', async (dialog) => {
|
||||
await dialog.accept();
|
||||
});
|
||||
|
||||
const deleteResponse = clickAndWaitForResponse(
|
||||
page,
|
||||
deleteButton,
|
||||
new RegExp(`/api/v1/domains/${domainId}`),
|
||||
{ status: 200 }
|
||||
);
|
||||
|
||||
await deleteResponse;
|
||||
});
|
||||
});
|
||||
|
||||
// Add DNS provider
|
||||
test('DNS Providers - list providers after API seed', async ({ page, testData }) => {
|
||||
const { name } = await testData.createDNSProvider({
|
||||
providerType: 'manual',
|
||||
name: 'Domain-Management-DNS',
|
||||
credentials: {},
|
||||
});
|
||||
|
||||
await test.step('Navigate to DNS providers page', async () => {
|
||||
await navigateToDnsProviders(page);
|
||||
});
|
||||
|
||||
await test.step('Verify provider card is visible', async () => {
|
||||
await waitForResourceInUI(page, name);
|
||||
await expect(page.getByRole('heading', { name })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
// Verify domain ownership
|
||||
test('DNS Providers - open form and load provider types', async ({ page }) => {
|
||||
await test.step('Navigate to DNS providers page', async () => {
|
||||
await navigateToDnsProviders(page);
|
||||
});
|
||||
|
||||
await test.step('Open add provider dialog', async () => {
|
||||
await page.request.get('/api/v1/dns-providers/types');
|
||||
const addButton = page.getByRole('button', { name: /add.*provider/i }).first();
|
||||
await addButton.click();
|
||||
await page.waitForSelector('[role="dialog"], form', { timeout: 3000 });
|
||||
await waitForModal(page, /provider/i);
|
||||
});
|
||||
|
||||
await test.step('Fill domain details', async () => {
|
||||
await page.getByLabel(/domain|name|hostname/i).fill('test.example.com');
|
||||
await test.step('Select provider type and verify credentials section', async () => {
|
||||
const providerType = page.getByRole('combobox', { name: /provider type/i }).first();
|
||||
await expect(providerType).toBeVisible();
|
||||
await providerType.click();
|
||||
|
||||
const descriptionField = page.getByLabel(/description|notes/i);
|
||||
if (await descriptionField.isVisible()) {
|
||||
await descriptionField.fill('Test domain');
|
||||
}
|
||||
});
|
||||
const manualOption = page.getByRole('option').filter({ hasText: /manual/i }).first();
|
||||
await expect(manualOption).toBeVisible();
|
||||
await manualOption.click();
|
||||
|
||||
await test.step('Submit domain', async () => {
|
||||
const submitButton = page.getByRole('button', { name: /create|submit|save/i }).first();
|
||||
await submitButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('Verify domain created', async () => {
|
||||
const domainElement = page.locator('text=test.example.com').first();
|
||||
await expect(domainElement).toBeVisible();
|
||||
await expect(page.getByTestId('credentials-section')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-402: View DNS records
|
||||
test('View DNS records for domain', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
// Renew SSL certificate
|
||||
test('DNS Providers - delete provider via API and verify removal', async ({ page, testData }) => {
|
||||
const { id, name } = await testData.createDNSProvider({
|
||||
providerType: 'manual',
|
||||
name: 'Delete-Provider-DNS',
|
||||
credentials: {},
|
||||
});
|
||||
|
||||
await test.step('Select domain to view records', async () => {
|
||||
const domainRow = page.locator('text=example.com').first();
|
||||
if (await domainRow.isVisible()) {
|
||||
const viewButton = domainRow.locator('..').getByRole('button', { name: /view|dns|records/i }).first();
|
||||
if (await viewButton.isVisible()) {
|
||||
await viewButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
}
|
||||
await test.step('Navigate to DNS providers page', async () => {
|
||||
await navigateToDnsProviders(page);
|
||||
});
|
||||
|
||||
await test.step('Verify DNS records displayed', async () => {
|
||||
const recordsSection = page.getByText(/dns|record|a\s+record|cname|mx/i).first();
|
||||
if (await recordsSection.isVisible()) {
|
||||
await expect(recordsSection).toBeVisible();
|
||||
}
|
||||
await test.step('Verify provider card is visible', async () => {
|
||||
const providerCard = page.locator('div').filter({
|
||||
has: page.getByRole('heading', { name }),
|
||||
}).first();
|
||||
await expect(providerCard).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Delete provider via API', async () => {
|
||||
await page.request.delete(`/api/v1/dns-providers/${id}`);
|
||||
});
|
||||
|
||||
await test.step('Verify provider card removed', async () => {
|
||||
await navigateToDnsProviders(page);
|
||||
await expect(page.getByRole('heading', { name })).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-403: Add DNS provider
|
||||
test('Add DNS provider configuration', async ({ page }) => {
|
||||
await test.step('Navigate to DNS provider settings', async () => {
|
||||
await page.goto('/settings', { waitUntil: 'networkidle' });
|
||||
|
||||
const dnsTab = page.getByRole('tab', { name: /dns|provider/i }).first();
|
||||
if (await dnsTab.isVisible()) {
|
||||
await dnsTab.click();
|
||||
}
|
||||
// View domain statistics
|
||||
test('Domains page renders heading and add form', async ({ page }) => {
|
||||
await test.step('Navigate to domains page', async () => {
|
||||
await navigateToDomains(page);
|
||||
});
|
||||
|
||||
await test.step('Click add provider button', async () => {
|
||||
const addButton = page.getByRole('button', { name: /add|create|new.*provider/i }).first();
|
||||
if (await addButton.isVisible()) {
|
||||
await addButton.click();
|
||||
await page.waitForSelector('[role="dialog"], form');
|
||||
}
|
||||
});
|
||||
await test.step('Verify heading and form controls', async () => {
|
||||
const heading = page.getByRole('heading', { name: /domains/i }).first();
|
||||
const input = page.getByRole('textbox').first();
|
||||
const addButton = page.getByRole('button', { name: /add domain/i }).first();
|
||||
|
||||
await test.step('Fill provider details', async () => {
|
||||
const providerSelect = page.locator('select[name*="provider"], [role="combobox"]').first();
|
||||
if (await providerSelect.isVisible()) {
|
||||
await providerSelect.selectOption(0); // Select first available
|
||||
}
|
||||
|
||||
const keyInput = page.getByLabel(/key|api.?key|token|credential/i).first();
|
||||
if (await keyInput.isVisible()) {
|
||||
// Don't fill with real credentials - just verify field
|
||||
expect(await keyInput.isVisible()).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
await test.step('Save provider', async () => {
|
||||
const saveButton = page.getByRole('button', { name: /save|create|add/i }).first();
|
||||
if (await saveButton.isVisible()) {
|
||||
await saveButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-404: Verify domain ownership
|
||||
test('Verify domain ownership', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
await test.step('Select domain to verify', async () => {
|
||||
const domainRow = page.locator('text=example.com').first();
|
||||
if (await domainRow.isVisible()) {
|
||||
const verifyButton = domainRow.locator('..').getByRole('button', { name: /verify|validate/i }).first();
|
||||
if (await verifyButton.isVisible()) {
|
||||
await verifyButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await test.step('Verify verification process shown', async () => {
|
||||
const verificationText = page.getByText(/dns.*txt|cname|verify|valid/i).first();
|
||||
if (await verificationText.isVisible()) {
|
||||
await expect(verificationText).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-405: Renew SSL certificate
|
||||
test('Renew SSL certificate for domain', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
await test.step('Find domain with certificate', async () => {
|
||||
const domainRow = page.locator('text=example.com').first();
|
||||
if (await domainRow.isVisible()) {
|
||||
const renewButton = domainRow.locator('..').getByRole('button', { name: /renew|refresh|certificate/i }).first();
|
||||
if (await renewButton.isVisible()) {
|
||||
await test.step('Click renew certificate', async () => {
|
||||
await renewButton.click();
|
||||
});
|
||||
|
||||
await test.step('Confirm renewal', async () => {
|
||||
const confirmButton = page.getByRole('button', { name: /confirm|renew|ok/i }).first();
|
||||
if (await confirmButton.isVisible()) {
|
||||
await confirmButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await test.step('Verify certificate status updated', async () => {
|
||||
const certStatus = page.getByText(/certificate|valid|renew/i).first();
|
||||
if (await certStatus.isVisible()) {
|
||||
await expect(certStatus).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-406: View domain statistics
|
||||
test('View domain statistics and status', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
await test.step('Open domain details', async () => {
|
||||
const domainRow = page.locator('text=example.com').first();
|
||||
if (await domainRow.isVisible()) {
|
||||
const statsButton = domainRow.locator('..').getByRole('button', { name: /view|details|stats/i }).first();
|
||||
if (await statsButton.isVisible()) {
|
||||
await statsButton.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await test.step('Verify stats displayed', async () => {
|
||||
const statsElements = page.locator('[data-testid*="stat"], [class*="stat"]');
|
||||
const count = await statsElements.count();
|
||||
expect(count).toBeGreaterThanOrEqual(0);
|
||||
|
||||
// Look for common stats
|
||||
const certStatus = page.getByText(/certificate|cert|expir/i).first();
|
||||
const dnsStatus = page.getByText(/dns|a\s+record|valid/i).first();
|
||||
|
||||
const hasStats = await certStatus.isVisible().catch(() => false)
|
||||
|| await dnsStatus.isVisible().catch(() => false);
|
||||
|
||||
expect(hasStats || count > 0).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-407: Disable domain temporarily
|
||||
test('Disable domain temporarily', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
await test.step('Find and disable domain', async () => {
|
||||
const domainRow = page.locator('text=example.com').first();
|
||||
if (await domainRow.isVisible()) {
|
||||
const disableToggle = domainRow.locator('..').locator('input[type="checkbox"]').first();
|
||||
if (await disableToggle.isVisible()) {
|
||||
const isChecked = await disableToggle.isChecked();
|
||||
if (isChecked) {
|
||||
await disableToggle.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await test.step('Verify domain disabled', async () => {
|
||||
const disabledIndicator = page.getByText(/inactive|disabled/i).first();
|
||||
if (await disabledIndicator.isVisible()) {
|
||||
await expect(disabledIndicator).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// UAT-408: Export domains as JSON
|
||||
test('Export domains configuration as JSON', async ({ page }) => {
|
||||
await test.step('Navigate to domains', async () => {
|
||||
await page.goto('/domains', { waitUntil: 'networkidle' });
|
||||
});
|
||||
|
||||
await test.step('Find export button', async () => {
|
||||
const exportButton = page.getByRole('button', { name: /export|download|json/i }).first();
|
||||
if (await exportButton.isVisible()) {
|
||||
// Set up listener for download
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
|
||||
await exportButton.click();
|
||||
|
||||
try {
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toMatch(/domain|config|export/i);
|
||||
} catch (e) {
|
||||
// Download might not trigger in test environment
|
||||
expect(true);
|
||||
}
|
||||
}
|
||||
await expect(heading).toBeVisible();
|
||||
await expect(input).toBeVisible();
|
||||
await expect(addButton).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user