Files
autotest/tests/pbx/extensions.spec.ts
T
2026-05-22 11:46:43 +02:00

104 lines
5.8 KiB
TypeScript

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.
});
});