New toggle under Settings → Behavior → Interactions: "Switch to card's bucket on open (search / link)". When enabled, openDetail calls activateBucket(card.bucket_id) so the board and bucket bar update to show the card's context. Skipped when already on 'all' or the correct bucket. Default off. Uses activateBucket() (from buckets.js) instead of raw state assignment so the bucket-bar highlight tracks the switch. Tests: two cases — setting on switches activeBucket + verifies the bucket-tab.active in the bar matches; setting off leaves the bucket unchanged. Both create/delete probe cards in try/finally and restore the setting + activeBucket in afterEach. Bumps to 1.9.27. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
152 lines
5.5 KiB
JavaScript
152 lines
5.5 KiB
JavaScript
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;
|
|
const b = buckets[0];
|
|
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.
|
|
const other = buckets.length > 1 ? buckets[1] : buckets[0];
|
|
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();
|
|
}
|
|
});
|
|
});
|