import { test, expect } from '@playwright/test'; import { login, BASE_URL } from '../../helpers/auth'; import { markManual, markPartial } from '../../helpers/manual'; const TEST_EXT_NUMBER = '8801'; const TEST_EXT_NAME = 'Autotest Extension'; test.describe('pbx-extensions', () => { test.beforeEach(async ({ page }) => { await login(page); }); test('pbx-extensions-check-create_modify_delete_sip_extensions', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'automated' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); testInfo.annotations.push({ type: 'description', description: 'Verify full CRUD lifecycle (Create, Modify, Delete) for SIP extensions in the admin portal' }); await test.step('Navigate to Extensions section', async () => { await page.goto(`${BASE_URL}/admin/extensions`); }); await test.step('Create a new SIP extension', async () => { await page.locator('button:has-text("Add"),button:has-text("New Extension"),a:has-text("Add Extension")').first().click(); await page.locator('[name*="extension"],[name*="ext_number"],[id*="extension"]').first().fill(TEST_EXT_NUMBER); await page.locator('[name*="name"],[name*="display_name"],[id*="name"]').first().fill(TEST_EXT_NAME); await page.locator('[name*="password"],[id*="sip-password"]').first().fill('Test@12345!'); await page.locator('button:has-text("Save"),button[type="submit"]').first().click(); }); await test.step('Verify new extension appears in the list', async () => { await page.goto(`${BASE_URL}/admin/extensions`); await expect(page.locator('table, .extension-list').filter({ hasText: TEST_EXT_NUMBER })).toBeVisible(); }); await test.step('Edit the extension — change display name', async () => { await page.locator('tr, .extension-item').filter({ hasText: TEST_EXT_NUMBER }).locator('a:has-text("Edit"),button:has-text("Edit")').first().click(); const nameInput = page.locator('[name*="name"],[name*="display_name"]').first(); await nameInput.clear(); await nameInput.fill(`${TEST_EXT_NAME} Modified`); await page.locator('button:has-text("Save"),button[type="submit"]').first().click(); }); await test.step('Verify modified name is persisted', async () => { await page.goto(`${BASE_URL}/admin/extensions`); await expect(page.locator('body')).toContainText(`${TEST_EXT_NAME} Modified`); }); await test.step('Delete the test extension', async () => { await page.locator('tr, .extension-item').filter({ hasText: TEST_EXT_NUMBER }).locator('button:has-text("Delete"),a:has-text("Delete")').first().click(); await page.locator('button:has-text("Confirm"),button:has-text("Yes"),button:has-text("Delete")').last().click(); }); await test.step('Verify extension is removed from the list', async () => { await page.goto(`${BASE_URL}/admin/extensions`); await expect(page.locator('body')).not.toContainText(TEST_EXT_NUMBER); }); }); test('pbx-extensions-check-extension_registration_with_deskphones_softphones', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'manual' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); markManual('Requires physical SIP desk phone (e.g., Yealink, Polycom, Snom) or software SIP client (Zoiper, MicroSIP) — registration cannot be simulated by Playwright'); testInfo.annotations.push({ type: 'description', description: 'Verify a SIP extension registers successfully from a desk phone and from a softphone client' }); // Manual steps: // 1. On a physical desk phone: enter PBX IP, extension number, and SIP password. // 2. Verify phone shows "Registered" status on its LCD. // 3. In admin portal → Extensions, verify the extension shows "Registered" status. // 4. Repeat with a softphone (Zoiper or MicroSIP) on a laptop. // 5. Record: phone model / firmware, registration time, portal status. }); test('pbx-extensions-check-device_provisioning', async ({ page }, testInfo) => { testInfo.annotations.push({ type: 'automationStatus', description: 'partial' }); testInfo.annotations.push({ type: 'testType', description: 'check' }); markPartial('Provisioning profile creation and URL configuration can be verified via admin portal; actual device auto-provisioning requires a physical phone that fetches the config'); testInfo.annotations.push({ type: 'description', description: 'Verify automatic device provisioning: provisioning server URL accessible, profile generated correctly' }); await test.step('Navigate to Provisioning settings', async () => { await page.goto(`${BASE_URL}/admin/provisioning`); }); await test.step('Verify provisioning is enabled', async () => { await expect(page.locator('body')).not.toContainText(/500|not found/i); await expect(page.locator('main, .content').first()).toBeVisible(); }); await test.step('Verify provisioning server URL is configured', async () => { const urlField = page.locator('[name*="prov_url"],[name*="provisioning_url"],[id*="prov-url"]').first(); if (await urlField.count() > 0) { const value = await urlField.inputValue(); expect(value).toMatch(/^https?:\/\//); } }); await test.step('Verify provisioning templates are listed for at least one phone brand', async () => { const templates = page.locator('.template-item, .device-template, tr').filter({ hasText: /yealink|polycom|snom|grandstream|cisco|fanvil/i }); await expect(templates.first()).toBeVisible(); }); // TODO_MANUAL: Connect a physical SIP phone to the same network, configure it to fetch // provisioning from the server URL, reboot, and verify it registers automatically. }); });