import { describe, it, afterEach, expect, $, click, pressKey, sleep, closeAll, csrf } from './runner.js?v=4'; describe('Detail View', () => { afterEach(() => closeAll()); it('has structural elements', () => { for (const id of ['detail', 'detail-title', 'detail-body', 'detail-meta', 'detail-tags']) { expect($(`#${id}`)).toExist(); } }); it('has control buttons', () => { for (const id of ['detail-edit', 'detail-delete', 'detail-done', 'detail-zen', 'detail-close']) { expect($(`#${id}`)).toExist(); } }); it('opens on card click (non-split)', async () => { if (!$('#board-preview')?.classList.contains('hidden')) { click('#board-split-toggle'); await sleep(100); } const card = $('#board [data-id]'); if (!card) return; click(card); await sleep(300); expect($('#detail')).toBeVisible(); }); it('shows card title', async () => { if (!$('#board-preview')?.classList.contains('hidden')) { click('#board-split-toggle'); await sleep(100); } const card = $('#board [data-id]'); if (!card) return; click(card); await sleep(300); expect($('#detail-title').textContent.length).toBeGreaterThan(0); }); it('closes on ESC', async () => { if (!$('#board-preview')?.classList.contains('hidden')) { click('#board-split-toggle'); await sleep(100); } const card = $('#board [data-id]'); if (!card) return; click(card); await sleep(300); if (!$('#detail')?.classList.contains('open')) return; pressKey('Escape'); for (let i = 0; i < 20; i++) { if (!$('#detail')?.classList.contains('open')) break; await sleep(50); } expect($('#detail')).toBeHidden(); }); it('closes on close button', async () => { if (!$('#board-preview')?.classList.contains('hidden')) { click('#board-split-toggle'); await sleep(100); } const card = $('#board [data-id]'); if (!card) return; click(card); await sleep(300); click('#detail-close'); await sleep(150); expect($('#detail')).toBeHidden(); }); }); describe('Detail: open-switches-bucket', () => { const SETTING_KEY = 'hyprcrd-open-switches-bucket'; const BUCKET_KEY = 'hyprcrd-active-bucket'; afterEach(async () => { await closeAll(); // Restore runner baseline. window.state.activeBucket = 'all'; localStorage.setItem(BUCKET_KEY, 'all'); }); it('switches to the card bucket when the setting is on', async () => { const buckets = await fetch('api/buckets').then(r => r.json()); if (!Array.isArray(buckets) || !buckets.length) return; // The bucket bar renders top-level non-super buckets with // `data-bucket=`, super-bucket parents with `data-bucket-super`, // and hides child buckets behind the parent's dropdown. To keep // this test robust across different user bucket layouts, pick two // plain top-level buckets (no children, no parent). Skip when the // fixture doesn't give us two. const childParentIds = new Set(buckets.filter(x => x.parent_id).map(x => x.parent_id)); const plainTop = buckets.filter(x => !x.parent_id && !childParentIds.has(x.id)); if (plainTop.length < 2) return; const b = plainTop[0]; const other = plainTop[1]; const token = csrf(); const h = { 'Content-Type': 'application/json', 'X-CSRF-Token': token }; let card; const prev = localStorage.getItem(SETTING_KEY); try { card = await fetch('api/cards', { method: 'POST', headers: h, body: JSON.stringify({ title: '__test_switch_bucket__', body: '', bucket_id: b.id }) }).then(r => r.json()); await window._reloadAll(); await sleep(100); // Start on a DIFFERENT bucket so the switch is visible. window.state.activeBucket = String(other.id); // Enable the setting. localStorage.setItem(SETTING_KEY, '1'); window._openDetail(card.id); await sleep(150); expect(String(window.state.activeBucket)).toBe(String(b.id)); // Bucket bar must highlight the switched-to bucket. const activeTab = document.querySelector('#bucket-bar .bucket-tab.active[data-bucket]'); expect(activeTab).toExist(); expect(activeTab.dataset.bucket).toBe(String(b.id)); } finally { localStorage.setItem(SETTING_KEY, prev ?? '0'); if (card?.id) await fetch(`api/cards/${card.id}`, { method: 'DELETE', headers: { 'X-CSRF-Token': token } }); await window._reloadAll(); } }); it('does NOT switch when the setting is off', async () => { const buckets = await fetch('api/buckets').then(r => r.json()); if (!Array.isArray(buckets) || buckets.length < 2) return; const token = csrf(); const h = { 'Content-Type': 'application/json', 'X-CSRF-Token': token }; let card; const prev = localStorage.getItem(SETTING_KEY); try { card = await fetch('api/cards', { method: 'POST', headers: h, body: JSON.stringify({ title: '__test_no_switch__', body: '', bucket_id: buckets[0].id }) }).then(r => r.json()); await window._reloadAll(); await sleep(100); window.state.activeBucket = String(buckets[1].id); localStorage.setItem(SETTING_KEY, '0'); window._openDetail(card.id); await sleep(100); // Should stay on the other bucket. expect(String(window.state.activeBucket)).toBe(String(buckets[1].id)); } finally { localStorage.setItem(SETTING_KEY, prev ?? '0'); if (card?.id) await fetch(`api/cards/${card.id}`, { method: 'DELETE', headers: { 'X-CSRF-Token': token } }); await window._reloadAll(); } }); });