import { test, expect } from '@playwright/test'; import { login, BASE_URL } from '../../helpers/auth'; import { markManual, markPartial, markWrite } from '../../helpers/manual'; import { attachWriteResult } from '../../helpers/writeResult'; const DOCS_URL = process.env.DOCS_URL ?? `${BASE_URL}/docs`; test.describe('core-features', () => { test.beforeEach(async ({ page }) => { await login(page); }); test('core-features-write-summary_of_each_new_feature_per_release', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'automated' }); markWrite(); testInfo.annotations.push({ type: 'description', description: 'Extract and record a summary of each new feature included in this release' }); const captured: Record = {}; await test.step('Navigate to Release Notes / What\'s New section', async () => { await page.goto(`${DOCS_URL}/release-notes`); captured['url'] = page.url(); }); await test.step('Extract feature list items', async () => { const features = await page .locator('h2, h3, li, .feature-item') .filter({ hasText: /feature|new|added|introduced/i }) .allTextContents(); captured['newFeatures'] = features; captured['count'] = features.length; }); await test.step('Attach feature summary to report', async () => { await attachWriteResult(testInfo, captured, 'feature-summary'); }); expect(captured['count']).toBeGreaterThan(0); }); test('core-features-check-integration_of_new_features_with_existing_call_processing_and_modules', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'partial' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); markPartial('UI configuration and module settings can be verified via admin portal; actual call-flow integration requires active telephony endpoints outside Playwright scope'); testInfo.annotations.push({ type: 'description', description: 'Verify new features integrate correctly with existing call processing and system modules' }); await test.step('Navigate to admin portal modules section', async () => { await page.goto(`${BASE_URL}/admin/modules`); }); await test.step('Verify modules list is visible and no error state is shown', async () => { await expect(page.locator('body')).not.toContainText(/error|failed|unavailable/i); }); await test.step('Verify new feature settings are accessible in the portal', async () => { await expect(page.locator('main, .content').first()).toBeVisible(); }); // TODO_MANUAL: For each new feature, trigger a call flow that exercises it and // verify the call completes without errors in the call log. }); test('core-features-check-backward_compatibility_of_new_features', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'partial' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); markPartial('Configuration import compatibility and settings migration can be verified via admin portal; functional backward compatibility with older endpoint firmware requires manual telephony testing'); testInfo.annotations.push({ type: 'description', description: 'Verify new features are backward-compatible with existing configurations and older endpoint firmware' }); await test.step('Import a backup from the previous version', async () => { await page.goto(`${BASE_URL}/admin/backup`); await expect(page.locator('body')).not.toContainText(/error/i); }); await test.step('Verify configuration is shown without migration errors', async () => { await expect(page.locator('.alert-danger, .error-banner').first()).toHaveCount(0); }); // TODO_MANUAL: Register an older SIP endpoint (previous firmware) and verify it // still registers and can complete calls without errors. }); test('core-features-check-performance_and_resource_impact_assessment_of_new_features', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'manual' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); markManual('Requires dedicated SIP load generator (SIPp), performance monitoring infrastructure, and sustained load; cannot be executed with Playwright alone'); testInfo.annotations.push({ type: 'description', description: 'Assess CPU/memory/network resource impact of new features under representative call load' }); // Manual steps: // 1. Establish a baseline resource measurement with current stable build. // 2. Enable each new feature one at a time. // 3. Run SIPp with a representative concurrent-call scenario (e.g., 100 simultaneous calls). // 4. Capture CPU, RAM, and I/O metrics every 30 s for at least 10 minutes. // 5. Compare metrics against the baseline. // 6. Record any resource spike or degradation > 10% as a finding. }); test('core-features-write-analysis_of_new_security_risks', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'manual' }); markWrite(); markManual('Security risk analysis requires expert threat modelling, source code review, and human judgment; not automatable via browser automation'); testInfo.annotations.push({ type: 'description', description: 'Identify, analyse, and document new security risks introduced by features in this release' }); // Manual steps: // 1. Obtain the diff / commit list for this release. // 2. Review each new feature for new attack surfaces (new APIs, new ports, new auth flows). // 3. Run OWASP ZAP or equivalent against new endpoints. // 4. Document findings: risk level (Critical/High/Medium/Low), description, CVE if applicable. // 5. Record results in the security risk register. }); test('core-features-write-ui_ux_and_admin_portal_changes_validation', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'automated' }); markWrite(); testInfo.annotations.push({ type: 'description', description: 'Capture and record all UI/UX changes in the admin portal for this release (screenshots + text)' }); const captured: Record = {}; const navSections = ['dashboard', 'extensions', 'trunks', 'dialplans', 'settings', 'security', 'logs']; await test.step('Iterate admin portal main sections and capture screenshots', async () => { captured['sections'] = []; for (const section of navSections) { await page.goto(`${BASE_URL}/admin/${section}`); const title = await page.title(); const screenshot = await page.screenshot({ fullPage: false }); await testInfo.attach(`screenshot-${section}`, { body: screenshot, contentType: 'image/png' }); (captured['sections'] as string[]).push(`${section}: ${title}`); } }); await test.step('Attach section inventory to report', async () => { await attachWriteResult(testInfo, captured, 'ui-changes'); }); expect((captured['sections'] as string[]).length).toBeGreaterThan(0); }); test('core-features-check-provisioning_changes_for_new_features', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'automated' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); testInfo.annotations.push({ type: 'description', description: 'Verify provisioning settings and workflows for new features are accessible and correctly displayed in the admin portal' }); await test.step('Navigate to provisioning settings', async () => { await page.goto(`${BASE_URL}/admin/provisioning`); }); await test.step('Verify provisioning page loads without errors', async () => { await expect(page.locator('body')).not.toContainText(/500|error|not found/i); }); await test.step('Verify provisioning options are visible', async () => { await expect(page.locator('main, .content, form').first()).toBeVisible(); }); }); });