mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { syncContacts, getContacts, type Contact } from '../helpers/api';
|
|
|
|
/** Escape special regex characters in a string. */
|
|
function escapeRegex(s: string): string {
|
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
/** Find a named non-repeater contact (type 2 = repeater). */
|
|
function findChatContact(contacts: Contact[]): Contact | undefined {
|
|
return contacts.find((c) => c.name && c.name.trim().length > 0 && c.type !== 2);
|
|
}
|
|
|
|
test.describe('Contacts sidebar & info pane', () => {
|
|
test.beforeAll(async () => {
|
|
await syncContacts();
|
|
});
|
|
|
|
test('contacts appear in sidebar and clicking opens conversation', async ({ page }) => {
|
|
const contacts = await getContacts();
|
|
const named = findChatContact(contacts);
|
|
if (!named) {
|
|
test.skip(true, 'No named non-repeater contacts synced from radio');
|
|
return;
|
|
}
|
|
|
|
await page.goto('/');
|
|
await expect(page.getByText('Connected')).toBeVisible();
|
|
|
|
// Click the contact in the sidebar
|
|
await page.getByText(named.name!, { exact: true }).first().click();
|
|
|
|
// Verify composer placeholder says "Message [name]..."
|
|
const escapedName = escapeRegex(named.name!.trim());
|
|
await expect(
|
|
page.getByPlaceholder(new RegExp(`message\\s+${escapedName}`, 'i'))
|
|
).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test('contact info pane shows profile data', async ({ page }) => {
|
|
const contacts = await getContacts();
|
|
const named = findChatContact(contacts);
|
|
if (!named) {
|
|
test.skip(true, 'No named non-repeater contacts synced from radio');
|
|
return;
|
|
}
|
|
|
|
await page.goto('/');
|
|
await expect(page.getByText('Connected')).toBeVisible();
|
|
|
|
// Open contact conversation
|
|
await page.getByText(named.name!, { exact: true }).first().click();
|
|
const escapedName = escapeRegex(named.name!.trim());
|
|
await expect(
|
|
page.getByPlaceholder(new RegExp(`message\\s+${escapedName}`, 'i'))
|
|
).toBeVisible({ timeout: 10_000 });
|
|
|
|
// Click avatar to open contact info sheet
|
|
await page.locator('[title="View contact info"]').click();
|
|
|
|
// Verify sheet opens with public key text and type badge
|
|
// Scope to the Contact Info pane to avoid matching the header pubkey
|
|
const infoPane = page.getByLabel('Contact Info');
|
|
await expect(infoPane.locator('[title="Click to copy"]')).toBeVisible({ timeout: 10_000 });
|
|
await expect(infoPane.getByText(named.public_key.slice(0, 8))).toBeVisible();
|
|
});
|
|
|
|
test('copy public key from contact info pane', async ({ page }) => {
|
|
const contacts = await getContacts();
|
|
const named = findChatContact(contacts);
|
|
if (!named) {
|
|
test.skip(true, 'No named non-repeater contacts synced from radio');
|
|
return;
|
|
}
|
|
|
|
await page.goto('/');
|
|
await expect(page.getByText('Connected')).toBeVisible();
|
|
|
|
await page.getByText(named.name!, { exact: true }).first().click();
|
|
const escapedName = escapeRegex(named.name!.trim());
|
|
await expect(
|
|
page.getByPlaceholder(new RegExp(`message\\s+${escapedName}`, 'i'))
|
|
).toBeVisible({ timeout: 10_000 });
|
|
|
|
await page.locator('[title="View contact info"]').click();
|
|
|
|
// Grant clipboard permissions
|
|
await page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
|
|
|
|
// Click public key to copy (scope to Contact Info pane)
|
|
const infoPane = page.getByLabel('Contact Info');
|
|
const pubkeySpan = infoPane.locator('[title="Click to copy"]');
|
|
await expect(pubkeySpan).toBeVisible({ timeout: 10_000 });
|
|
await pubkeySpan.click();
|
|
|
|
// Verify toast
|
|
await expect(page.getByText('Public key copied!')).toBeVisible({ timeout: 5_000 });
|
|
});
|
|
});
|