| 1 |
const { describe, test, beforeEach } = require('node:test'); |
| 2 |
const assert = require('node:assert'); |
| 3 |
const { BB, resetMockElement, apiCalls, resetItemFixtures } = require('./setup'); |
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
beforeEach(() => { |
| 8 |
resetItemFixtures(); |
| 9 |
apiCalls.length = 0; |
| 10 |
}); |
| 11 |
|
| 12 |
describe('BB.items.load', () => { |
| 13 |
test('populates state with items', async () => { |
| 14 |
await BB.items.load(); |
| 15 |
assert.strictEqual(BB.state.items.length, 2); |
| 16 |
assert.strictEqual(BB.state.items[0].title, 'First'); |
| 17 |
assert.strictEqual(BB.state.hasMore, true); |
| 18 |
}); |
| 19 |
|
| 20 |
test('appends items when append=true', async () => { |
| 21 |
BB.state.set('items', [{ id: 'old', title: 'Old' }]); |
| 22 |
await BB.items.load(true); |
| 23 |
assert.strictEqual(BB.state.items.length, 3); |
| 24 |
assert.strictEqual(BB.state.items[0].id, 'old'); |
| 25 |
}); |
| 26 |
}); |
| 27 |
|
| 28 |
describe('BB.items.selectItem', () => { |
| 29 |
test('sets selectedItemId', async () => { |
| 30 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); |
| 31 |
await BB.items.selectItem('i1'); |
| 32 |
assert.strictEqual(BB.state.selectedItemId, 'i1'); |
| 33 |
}); |
| 34 |
|
| 35 |
test('marks item as read via API', async () => { |
| 36 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); |
| 37 |
await BB.items.selectItem('i1'); |
| 38 |
assert.ok(apiCalls.some(c => c.cmd === 'markRead' && c.id === 'i1')); |
| 39 |
}); |
| 40 |
}); |
| 41 |
|
| 42 |
describe('BB.items.toggleStar', () => { |
| 43 |
test('unstars a starred item', async () => { |
| 44 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: true }]); |
| 45 |
await BB.items.toggleStar('i1', true); |
| 46 |
assert.ok(apiCalls.some(c => c.cmd === 'unstar')); |
| 47 |
assert.strictEqual(BB.state.items[0].isStarred, false); |
| 48 |
}); |
| 49 |
|
| 50 |
test('stars an unstarred item', async () => { |
| 51 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: false }]); |
| 52 |
await BB.items.toggleStar('i1', false); |
| 53 |
assert.ok(apiCalls.some(c => c.cmd === 'star')); |
| 54 |
assert.strictEqual(BB.state.items[0].isStarred, true); |
| 55 |
}); |
| 56 |
}); |
| 57 |
|
| 58 |
describe('BB.items.toggleRead', () => { |
| 59 |
test('marks read item as unread', async () => { |
| 60 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: true, isStarred: false }]); |
| 61 |
await BB.items.toggleRead('i1', true); |
| 62 |
assert.ok(apiCalls.some(c => c.cmd === 'markUnread')); |
| 63 |
assert.strictEqual(BB.state.items[0].isRead, false); |
| 64 |
}); |
| 65 |
|
| 66 |
test('marks unread item as read', async () => { |
| 67 |
BB.state.set('items', [{ id: 'i1', title: 'T', isRead: false, isStarred: false }]); |
| 68 |
await BB.items.toggleRead('i1', false); |
| 69 |
assert.ok(apiCalls.some(c => c.cmd === 'markRead' && c.id === 'i1')); |
| 70 |
assert.strictEqual(BB.state.items[0].isRead, true); |
| 71 |
}); |
| 72 |
}); |
| 73 |
|
| 74 |
describe('BB.items.loadMore', () => { |
| 75 |
test('increments page', async () => { |
| 76 |
BB.state.set('currentPage', 0); |
| 77 |
await BB.items.loadMore(); |
| 78 |
assert.strictEqual(BB.state.currentPage, 1); |
| 79 |
}); |
| 80 |
}); |
| 81 |
|
| 82 |
describe('BB.items.render', () => { |
| 83 |
test('empty items array renders placeholder message', () => { |
| 84 |
resetMockElement('items-list'); |
| 85 |
BB.state.set('sources', [{ id: 's1', name: 'F' }]); |
| 86 |
BB.items.render([]); |
| 87 |
const list = document.getElementById('items-list'); |
| 88 |
assert.ok(list.innerHTML.includes('empty-state'), 'Should show empty state'); |
| 89 |
}); |
| 90 |
|
| 91 |
test('item with isStarred=true has starred class', () => { |
| 92 |
resetMockElement('items-list'); |
| 93 |
BB.items.render([ |
| 94 |
{ id: 'i1', title: 'T', author: 'A', isRead: false, isStarred: true, timeAgo: '1m' }, |
| 95 |
]); |
| 96 |
const list = document.getElementById('items-list'); |
| 97 |
const item = list.children[0]; |
| 98 |
assert.ok(item.innerHTML.includes('starred'), 'Should have starred class'); |
| 99 |
}); |
| 100 |
|
| 101 |
test('item with isRead=true has read class', () => { |
| 102 |
resetMockElement('items-list'); |
| 103 |
BB.items.render([ |
| 104 |
{ id: 'i1', title: 'T', author: 'A', isRead: true, isStarred: false, timeAgo: '1m' }, |
| 105 |
]); |
| 106 |
const list = document.getElementById('items-list'); |
| 107 |
const item = list.children[0]; |
| 108 |
assert.ok(item.className.includes('read'), 'Should have read class'); |
| 109 |
}); |
| 110 |
|
| 111 |
test('loadMore with hasMore=false is a no-op for display', () => { |
| 112 |
resetMockElement('items-list'); |
| 113 |
BB.state.set('hasMore', false); |
| 114 |
const loadMoreEl = document.getElementById('load-more'); |
| 115 |
BB.items.render([{ id: 'i1', title: 'T', author: 'A', isRead: false, isStarred: false, timeAgo: '1m' }]); |
| 116 |
assert.strictEqual(loadMoreEl.style.display, 'none'); |
| 117 |
}); |
| 118 |
}); |
| 119 |
|