const { describe, test, beforeEach } = require('node:test'); const assert = require('node:assert'); const { BB, resetMockElement, apiCalls, resetItemFixtures } = require('./setup'); // The item mocks write back to their fixtures, so each test starts from the // same two items rather than from whatever the last toggle left behind. beforeEach(() => { resetItemFixtures(); apiCalls.length = 0; }); describe('BB.items.load', () => { test('populates state with items', async () => { await BB.items.load(); assert.strictEqual(BB.state.items.length, 2); assert.strictEqual(BB.state.items[0].title, 'First'); assert.strictEqual(BB.state.hasMore, true); }); test('appends items when append=true', async () => { BB.state.set('items', [{ id: 'old', title: 'Old' }]); await BB.items.load(true); assert.strictEqual(BB.state.items.length, 3); assert.strictEqual(BB.state.items[0].id, 'old'); }); }); describe('BB.items.selectItem', () => { test('sets selectedItemId', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); await BB.items.selectItem('i1'); assert.strictEqual(BB.state.selectedItemId, 'i1'); }); test('marks item as read via API', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); await BB.items.selectItem('i1'); assert.ok(apiCalls.some(c => c.cmd === 'markRead' && c.id === 'i1')); }); }); describe('BB.items.toggleStar', () => { test('unstars a starred item', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: true }]); await BB.items.toggleStar('i1', true); assert.ok(apiCalls.some(c => c.cmd === 'unstar')); assert.strictEqual(BB.state.items[0].isStarred, false); }); test('stars an unstarred item', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: false }]); await BB.items.toggleStar('i1', false); assert.ok(apiCalls.some(c => c.cmd === 'star')); assert.strictEqual(BB.state.items[0].isStarred, true); }); }); describe('BB.items.toggleRead', () => { test('marks read item as unread', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: false }]); await BB.items.toggleRead('i1', true); assert.ok(apiCalls.some(c => c.cmd === 'markUnread')); assert.strictEqual(BB.state.items[0].isRead, false); }); test('marks unread item as read', async () => { BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); await BB.items.toggleRead('i1', false); assert.ok(apiCalls.some(c => c.cmd === 'markRead' && c.id === 'i1')); assert.strictEqual(BB.state.items[0].isRead, true); }); }); describe('BB.items.loadMore', () => { test('increments page', async () => { BB.state.set('currentPage', 0); await BB.items.loadMore(); assert.strictEqual(BB.state.currentPage, 1); }); }); describe('BB.items.render', () => { test('empty items array renders placeholder message', () => { resetMockElement('items-list'); BB.state.set('sources', [{ id: 's1', name: 'F' }]); BB.items.render([]); const list = document.getElementById('items-list'); assert.ok(list.innerHTML.includes('empty-state'), 'Should show empty state'); }); test('item with isStarred=true has starred class', () => { resetMockElement('items-list'); BB.items.render([ { id: 'i1', title: 'T', author: 'A', isRead: false, isStarred: true, timeAgo: '1m' }, ]); const list = document.getElementById('items-list'); const item = list.children[0]; assert.ok(item.innerHTML.includes('starred'), 'Should have starred class'); }); test('item with isRead=true has read class', () => { resetMockElement('items-list'); BB.items.render([ { id: 'i1', title: 'T', author: 'A', isRead: true, isStarred: false, timeAgo: '1m' }, ]); const list = document.getElementById('items-list'); const item = list.children[0]; assert.ok(item.className.includes('read'), 'Should have read class'); }); test('loadMore with hasMore=false is a no-op for display', () => { resetMockElement('items-list'); BB.state.set('hasMore', false); const loadMoreEl = document.getElementById('load-more'); BB.items.render([{ id: 'i1', title: 'T', author: 'A', isRead: false, isStarred: false, timeAgo: '1m' }]); assert.strictEqual(loadMoreEl.style.display, 'none'); }); });