Make a better integration/fanout selector

This commit is contained in:
jkingsman
2026-03-24 13:48:50 -07:00
parent b022aea71f
commit e8a4f5c349
6 changed files with 501 additions and 269 deletions
+13
View File
@@ -1,6 +1,10 @@
import type { Locator, Page } from '@playwright/test';
import http from 'http';
function escapeRegex(text: string): string {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
export function createCaptureServer(urlFactory: (port: number) => string) {
const requests: { body: string; headers: http.IncomingHttpHeaders }[] = [];
const server = http.createServer((req, res) => {
@@ -38,6 +42,15 @@ export async function openFanoutSettings(page: Page): Promise<void> {
await page.getByRole('button', { name: /MQTT.*Automation/ }).click();
}
export async function startIntegrationDraft(page: Page, integrationName: string): Promise<void> {
await page.getByRole('button', { name: 'Add Integration' }).click();
const dialog = page.getByRole('dialog', { name: 'Create Integration' });
await dialog
.getByRole('button', { name: new RegExp(`^${escapeRegex(integrationName)}(?:\\s|$)`) })
.click();
await dialog.getByRole('button', { name: 'Create' }).click();
}
export function fanoutHeader(page: Page, name: string): Locator {
const nameButton = page.getByRole('button', { name, exact: true });
return page
+7 -4
View File
@@ -4,7 +4,12 @@ import {
deleteFanoutConfig,
getFanoutConfigs,
} from '../helpers/api';
import { createCaptureServer, fanoutHeader, openFanoutSettings } from '../helpers/fanout';
import {
createCaptureServer,
fanoutHeader,
openFanoutSettings,
startIntegrationDraft,
} from '../helpers/fanout';
test.describe('Apprise integration settings', () => {
let createdAppriseId: string | null = null;
@@ -35,9 +40,7 @@ test.describe('Apprise integration settings', () => {
await openFanoutSettings(page);
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
// Open add menu and pick Apprise
await page.getByRole('button', { name: 'Add Integration' }).click();
await page.getByRole('menuitem', { name: 'Apprise' }).click();
await startIntegrationDraft(page, 'Apprise');
// Should navigate to the detail/edit view with a numbered default name
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Apprise #\d+/);
+22 -20
View File
@@ -1,9 +1,10 @@
import { test, expect } from '@playwright/test';
import {
ensureFlightlessChannel,
createFanoutConfig,
deleteFanoutConfig,
getFanoutConfigs,
} from '../helpers/api';
import { openFanoutSettings, startIntegrationDraft } from '../helpers/fanout';
const BOT_CODE = `def bot(sender_name, sender_key, message_text, is_dm, channel_key, channel_name, sender_timestamp, path):
if channel_name == "#flightless" and "!e2etest" in message_text.lower():
@@ -28,32 +29,35 @@ test.describe('Bot functionality', () => {
}
});
test('create a bot via API, verify it in UI, trigger it, and verify response', async ({
test('create a bot via UI, trigger it, and verify response', async ({
page,
}) => {
// --- Step 1: Create and enable bot via fanout API ---
const bot = await createFanoutConfig({
type: 'bot',
name: 'E2E Test Bot',
config: { code: BOT_CODE },
enabled: true,
});
createdBotId = bot.id;
// --- Step 2: Verify bot appears in settings UI ---
await page.goto('/');
await openFanoutSettings(page);
await expect(page.getByRole('status', { name: 'Radio OK' })).toBeVisible();
await page.getByText('Settings').click();
await page.getByRole('button', { name: /MQTT.*Automation/ }).click();
await startIntegrationDraft(page, 'Python Bot');
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Python Bot #\d+/);
await page.locator('#fanout-edit-name').fill('E2E Test Bot');
const codeEditor = page.getByLabel('Bot code editor');
await codeEditor.click();
await codeEditor.press(process.platform === 'darwin' ? 'Meta+A' : 'Control+A');
await codeEditor.fill(BOT_CODE);
await page.getByRole('button', { name: /Save as Enabled/i }).click();
await expect(page.getByText('Integration saved and enabled')).toBeVisible();
// The bot name should be visible in the integration list
await expect(page.getByText('E2E Test Bot')).toBeVisible();
// Exit settings page mode
const configs = await getFanoutConfigs();
const createdBot = configs.find((config) => config.name === 'E2E Test Bot');
if (createdBot) {
createdBotId = createdBot.id;
}
await page.getByRole('button', { name: /Back to Chat/i }).click();
// --- Step 3: Trigger the bot ---
await page.getByText('#flightless', { exact: true }).first().click();
const triggerMessage = `!e2etest ${Date.now()}`;
@@ -61,8 +65,6 @@ test.describe('Bot functionality', () => {
await input.fill(triggerMessage);
await page.getByRole('button', { name: 'Send', exact: true }).click();
// --- Step 4: Verify bot response appears ---
// Bot has ~2s delay before responding, plus radio send time
await expect(page.getByText('[BOT] e2e-ok')).toBeVisible({ timeout: 30_000 });
});
});
+8 -6
View File
@@ -4,7 +4,12 @@ import {
deleteFanoutConfig,
getFanoutConfigs,
} from '../helpers/api';
import { createCaptureServer, fanoutHeader, openFanoutSettings } from '../helpers/fanout';
import {
createCaptureServer,
fanoutHeader,
openFanoutSettings,
startIntegrationDraft,
} from '../helpers/fanout';
test.describe('Webhook integration settings', () => {
let createdWebhookId: string | null = null;
@@ -35,9 +40,7 @@ test.describe('Webhook integration settings', () => {
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();
await startIntegrationDraft(page, 'Webhook');
// Should navigate to the detail/edit view with a numbered default name
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Webhook #\d+/);
@@ -77,8 +80,7 @@ test.describe('Webhook integration settings', () => {
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 startIntegrationDraft(page, 'Webhook');
await expect(page.locator('#fanout-edit-name')).toHaveValue(/Webhook #\d+/);
await page.locator('#fanout-edit-name').fill('Unsaved Webhook Draft');