mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
196 lines
7.0 KiB
TypeScript
196 lines
7.0 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import {
|
|
createFanoutConfig,
|
|
deleteFanoutConfig,
|
|
getFanoutConfigs,
|
|
} from '../helpers/api';
|
|
import { createCaptureServer, fanoutHeader, openFanoutSettings } from '../helpers/fanout';
|
|
|
|
test.describe('Webhook integration settings', () => {
|
|
let createdWebhookId: string | null = null;
|
|
let receiver: ReturnType<typeof createCaptureServer>;
|
|
let webhookUrl: string;
|
|
|
|
test.beforeAll(async () => {
|
|
receiver = createCaptureServer((port) => `http://127.0.0.1:${port}`);
|
|
webhookUrl = await receiver.listen();
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
receiver.close();
|
|
});
|
|
|
|
test.afterEach(async () => {
|
|
if (createdWebhookId) {
|
|
try {
|
|
await deleteFanoutConfig(createdWebhookId);
|
|
} catch {
|
|
console.warn('Failed to delete test webhook');
|
|
}
|
|
createdWebhookId = null;
|
|
}
|
|
});
|
|
|
|
test('create webhook via UI, configure, save as enabled, verify in list', async ({ page }) => {
|
|
await openFanoutSettings(page);
|
|
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
|
|
|
|
// Open add menu and pick Webhook
|
|
await page.getByRole('button', { name: 'Add Integration' }).click();
|
|
await page.getByRole('menuitem', { name: 'Webhook' }).click();
|
|
|
|
// Should navigate to the detail/edit view with a numbered default name
|
|
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Webhook #\d+/);
|
|
|
|
// Fill in webhook URL
|
|
const urlInput = page.locator('#fanout-webhook-url');
|
|
await urlInput.fill(webhookUrl);
|
|
|
|
// Verify method defaults to POST
|
|
await expect(page.locator('#fanout-webhook-method')).toHaveValue('POST');
|
|
|
|
// Rename it
|
|
const nameInput = page.locator('#fanout-edit-name');
|
|
await nameInput.clear();
|
|
await nameInput.fill('E2E Webhook');
|
|
|
|
// Save as enabled
|
|
await page.getByRole('button', { name: /Save as Enabled/i }).click();
|
|
await expect(page.getByText('Integration saved and enabled')).toBeVisible();
|
|
|
|
// Should be back on list view with our webhook visible
|
|
await expect(page.getByText('E2E Webhook')).toBeVisible();
|
|
await expect(page.getByText(webhookUrl)).toBeVisible();
|
|
|
|
// Clean up via API
|
|
const configs = await getFanoutConfigs();
|
|
const webhook = configs.find((c) => c.name === 'E2E Webhook');
|
|
if (webhook) {
|
|
createdWebhookId = webhook.id;
|
|
}
|
|
});
|
|
|
|
test('leaving a new webhook draft does not create a persisted config', async ({ page }) => {
|
|
const existingConfigs = await getFanoutConfigs();
|
|
const existingIds = new Set(existingConfigs.map((cfg) => cfg.id));
|
|
|
|
await openFanoutSettings(page);
|
|
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
|
|
|
|
await page.getByRole('button', { name: 'Add Integration' }).click();
|
|
await page.getByRole('menuitem', { name: 'Webhook' }).click();
|
|
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Webhook #\d+/);
|
|
|
|
await page.locator('#fanout-edit-name').fill('Unsaved Webhook Draft');
|
|
await page.locator('#fanout-webhook-url').fill(webhookUrl);
|
|
|
|
page.once('dialog', (dialog) => dialog.accept());
|
|
await page.getByText('← Back to list').click();
|
|
await expect(page.getByText('Unsaved Webhook Draft')).not.toBeVisible();
|
|
|
|
const updatedConfigs = await getFanoutConfigs();
|
|
const newConfigs = updatedConfigs.filter((cfg) => !existingIds.has(cfg.id));
|
|
expect(newConfigs).toHaveLength(0);
|
|
expect(updatedConfigs.find((cfg) => cfg.name === 'Unsaved Webhook Draft')).toBeUndefined();
|
|
});
|
|
|
|
test('create webhook via API, edit in UI, save as disabled', async ({ page }) => {
|
|
// Create via API
|
|
const webhook = await createFanoutConfig({
|
|
type: 'webhook',
|
|
name: 'API Webhook',
|
|
config: { url: webhookUrl, method: 'POST', headers: {} },
|
|
enabled: true,
|
|
});
|
|
createdWebhookId = webhook.id;
|
|
|
|
await openFanoutSettings(page);
|
|
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
|
|
|
|
// Click Edit on our webhook
|
|
const row = fanoutHeader(page, 'API Webhook');
|
|
await expect(row).toBeVisible();
|
|
await row.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// Should be in edit view
|
|
await expect(page.locator('#fanout-edit-name')).toHaveValue('API Webhook');
|
|
|
|
// Change method to PUT
|
|
await page.locator('#fanout-webhook-method').selectOption('PUT');
|
|
|
|
// Save as disabled
|
|
await page.getByRole('button', { name: /Save as Disabled/i }).click();
|
|
await expect(page.locator('#fanout-edit-name')).not.toBeVisible();
|
|
await expect(row).toContainText('Disabled');
|
|
|
|
// Verify it's now disabled in the list
|
|
const configs = await getFanoutConfigs();
|
|
const updated = configs.find((c) => c.id === webhook.id);
|
|
expect(updated?.enabled).toBe(false);
|
|
expect(updated?.config.method).toBe('PUT');
|
|
});
|
|
|
|
test('webhook shows scope selector with channel/contact options', async ({ page }) => {
|
|
const webhook = await createFanoutConfig({
|
|
type: 'webhook',
|
|
name: 'Scope Webhook',
|
|
config: { url: webhookUrl, method: 'POST', headers: {} },
|
|
});
|
|
createdWebhookId = webhook.id;
|
|
|
|
await openFanoutSettings(page);
|
|
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
|
|
|
|
// Click Edit
|
|
const row = fanoutHeader(page, 'Scope Webhook');
|
|
await expect(row).toBeVisible();
|
|
await row.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// Verify scope selector is visible with the three webhook-applicable modes
|
|
await expect(page.getByText('Message Scope')).toBeVisible();
|
|
await expect(page.getByText('All messages')).toBeVisible();
|
|
await expect(page.getByText('Only listed channels/contacts')).toBeVisible();
|
|
await expect(page.getByText('All except listed channels/contacts')).toBeVisible();
|
|
|
|
// Select "Only listed" to see channel/contact checkboxes
|
|
await page.getByText('Only listed channels/contacts').click();
|
|
|
|
// Should show Channels section (Contacts only appears if non-repeater contacts exist)
|
|
await expect(page.getByText('Channels (include)')).toBeVisible();
|
|
|
|
// Go back without saving
|
|
page.once('dialog', (dialog) => dialog.accept());
|
|
await page.getByText('← Back to list').click();
|
|
await expect(row).toBeVisible();
|
|
});
|
|
|
|
test('delete webhook via UI', async ({ page }) => {
|
|
const webhook = await createFanoutConfig({
|
|
type: 'webhook',
|
|
name: 'Delete Me Webhook',
|
|
config: { url: webhookUrl, method: 'POST', headers: {} },
|
|
});
|
|
createdWebhookId = webhook.id;
|
|
|
|
await openFanoutSettings(page);
|
|
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
|
|
|
|
// Click Edit
|
|
const row = fanoutHeader(page, 'Delete Me Webhook');
|
|
await expect(row).toBeVisible();
|
|
await row.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// Accept the confirmation dialog
|
|
page.once('dialog', (dialog) => dialog.accept());
|
|
|
|
// Click Delete
|
|
await page.getByRole('button', { name: 'Delete' }).click();
|
|
|
|
// Should be back on list, webhook gone
|
|
await expect(row).not.toBeVisible();
|
|
|
|
// Already deleted, clear the cleanup reference
|
|
createdWebhookId = null;
|
|
});
|
|
});
|