// ============================================================ // SETTINGS + FAQ + DICTATION — detailed UI exercises that // don't depend on real AI / recording permissions. // ============================================================ const { test, expect, E2E_BASE, mockAI } = require('../fixtures'); async function openTab(page, name) { await page.goto(E2E_BASE + '/'); await page.waitForSelector('button.tab-btn', { timeout: 15000 }); const vp = page.viewportSize(); if (vp && vp.width <= 768) { await page.click('#btn-menu-toggle').catch(() => {}); } await page.click(`button.tab-btn[data-tab="${name}"]`); await page.waitForFunction((t) => { const el = document.getElementById(t + '-tab'); return el && el.classList.contains('active') && el.innerHTML.trim().length > 100; }, name, { timeout: 15000 }); } test.describe('Settings — voice, password, nextcloud sections render', () => { test('voice-preferences inputs + save button exist', async ({ authedPage: _, page }) => { await openTab(page, 'settings'); // STT and TTS dropdowns populate; save button exists await expect(page.locator('#stt-model-select')).toBeVisible(); await expect(page.locator('#tts-voice-select')).toBeVisible(); await expect(page.locator('#btn-save-voice-prefs')).toBeVisible(); }); test('change-password form: all three fields + submit button present', async ({ authedPage: _, page }) => { await openTab(page, 'settings'); await expect(page.locator('#pw-current')).toBeVisible(); await expect(page.locator('#pw-new')).toBeVisible(); await expect(page.locator('#pw-confirm')).toBeVisible(); await expect(page.locator('#btn-change-password')).toBeVisible(); }); test('2FA setup panel has QR container + verify input', async ({ authedPage: _, page }) => { await openTab(page, 'settings'); // At least one of the 2FA buttons should be present const setupCount = await page.locator('#btn-setup-2fa').count(); const disableCount = await page.locator('#btn-disable-2fa').count(); expect(setupCount + disableCount).toBeGreaterThan(0); }); test('Nextcloud section: URL/user/pass fields render', async ({ authedPage: _, page }) => { await openTab(page, 'settings'); await expect(page.locator('#nc-url')).toBeVisible(); await expect(page.locator('#nc-user')).toBeVisible(); await expect(page.locator('#nc-pass')).toBeVisible(); }); }); test.describe('FAQ — questions expand + collapse on click', () => { test('clicking a FAQ question reveals its answer panel', async ({ authedPage: _, page }) => { await openTab(page, 'faq'); const questions = page.locator('.faq-question'); const count = await questions.count(); expect(count).toBeGreaterThan(0); const first = questions.first(); await first.click(); // The corresponding answer should become visible — either via class toggle or // inline style. Grab the sibling / next matching answer element and check. const ariaControls = await first.getAttribute('aria-controls'); if (ariaControls) { await expect(page.locator('#' + ariaControls)).toBeVisible(); } else { // Fallback: any .faq-answer becomes visible await expect(page.locator('.faq-answer').first()).toBeVisible(); } }); }); test.describe('Dictation — UI loads, transcript editable, clear works', () => { test('age/gender/setting inputs + generate + clear all render', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page, 'dictation'); await expect(page.locator('#dict-age')).toBeVisible(); await expect(page.locator('#dict-gender')).toBeVisible(); await expect(page.locator('#dict-setting')).toBeVisible(); await expect(page.locator('#dict-transcript')).toBeVisible(); await expect(page.locator('#dict-generate-btn')).toBeVisible(); }); test('typing into transcript + clear empties it', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page, 'dictation'); await page.locator('#dict-transcript').click(); await page.keyboard.type('Chief complaint: sore throat.'); await expect(page.locator('#dict-transcript')).toContainText('sore throat'); await page.click('#dict-clear'); const text = await page.locator('#dict-transcript').innerText(); expect(text.trim()).toBe(''); }); test('generate with short transcript → mocked HPI renders', async ({ authedPage: _, page }) => { await mockAI(page); await openTab(page, 'dictation'); await page.fill('#dict-age', '6 years'); await page.locator('#dict-transcript').click(); await page.keyboard.type('Patient with cough and congestion for 2 days.'); // Default output type is HPI — should call /api/generate-hpi-dictation const [resp] = await Promise.all([ page.waitForResponse('**/api/generate-hpi-dictation'), page.click('#dict-generate-btn'), ]); expect(resp.status()).toBe(200); await expect(page.locator('#dict-output')).toBeVisible(); await expect(page.locator('#dict-hpi-text')).toContainText('MOCK HPI from dictation'); }); });