import assert from 'node:assert/strict'; import test from 'node:test'; import { buildItems, type PaletteContext } from '../src/shell/paletteModel.ts'; import { X_COLUMN_KINDS, columnTitle, parseColumnValue, xColumnUrl } from '../src/shell/xColumns.ts'; import { columnUrl, normalizeDecks } from '../../../waybetter/packages/shell/src/deckModel.ts'; test('deck columns: every kind loads an x.com page', () => { assert.equal(xColumnUrl({ kind: 'home' }), 'https://x.com/home'); assert.equal(xColumnUrl({ kind: 'following' }), 'https://x.com/home', 'Following is X’s home tab, selected by a navigation-only click'); assert.equal(xColumnUrl({ kind: 'notifications' }), 'https://x.com/notifications'); assert.equal(xColumnUrl({ kind: 'mentions' }), 'https://x.com/notifications/mentions'); assert.equal(xColumnUrl({ kind: 'bookmarks' }), 'https://x.com/i/bookmarks'); assert.equal(xColumnUrl({ kind: 'list', value: '1234' }), 'https://x.com/i/lists/1234'); assert.equal(xColumnUrl({ kind: 'user', value: 'playfishies' }), 'https://x.com/playfishies'); assert.equal(xColumnUrl({ kind: 'search', value: 'from:me filter:media' }), 'https://x.com/search?q=from%3Ame%20filter%3Amedia&f=live'); for (const bad of [{ kind: 'list', value: 'abc' }, { kind: 'user', value: 'not a handle' }, { kind: 'search' }, { kind: 'rocket' }]) { assert.equal(xColumnUrl(bad), null, JSON.stringify(bad)); } for (const kind of X_COLUMN_KINDS) { const value = kind === 'list' ? '1' : kind === 'user' ? 'x' : kind === 'search' ? 'q' : undefined; assert.ok(columnUrl(value ? { kind, value } : { kind }, xColumnUrl)?.startsWith('https://x.com/'), kind); } }); test('deck columns: typed values are parsed per kind', () => { assert.equal(parseColumnValue('user', '@playfishies'), 'playfishies'); assert.equal(parseColumnValue('user', 'https://x.com/CRTkafa/status/1'), 'CRTkafa'); assert.equal(parseColumnValue('user', 'twitter.com/nope nope'), null); assert.equal(parseColumnValue('list', 'https://x.com/i/lists/1590000000000000001'), '1590000000000000001'); assert.equal(parseColumnValue('list', '42'), '42'); assert.equal(parseColumnValue('list', 'https://x.com/i/lists/abc'), null); assert.equal(parseColumnValue('search', ' wayBetter from:@playfishies '), 'wayBetter from:playfishies'); assert.equal(parseColumnValue('search', ' '), null); assert.equal(parseColumnValue('home', 'ignored'), ''); }); test('deck columns: header titles', () => { assert.deepEqual(columnTitle({ kind: 'notifications' }), { title: 'Notifications' }); assert.deepEqual(columnTitle({ kind: 'user', value: 'playfishies' }), { title: '@playfishies', detail: 'Profile' }); assert.deepEqual(columnTitle({ kind: 'search', value: 'gm' }), { title: 'gm', detail: 'Search · Latest' }); assert.deepEqual(columnTitle({ kind: 'home' }, 'Main feed'), { title: 'Main feed', detail: 'Home' }); }); test('deck storage: X kinds only, removed accounts fall back to the default', () => { const [deck] = normalizeDecks([{ id: 'd1', name: 'Deck', columns: [ { id: 'a', tabId: 'ka', profile: 'pgone', source: { kind: 'home' } }, { id: 'b', tabId: 'kb', profile: 'default', source: { kind: 'dms' } }, ] }], { kinds: X_COLUMN_KINDS, profiles: ['default'], fallbackProfile: 'default' }); assert.deepEqual(deck!.columns.map(c => [c.id, c.profile]), [['a', 'default']], 'no DM columns'); }); const base: PaletteContext = { tabs: [{ id: 't1', title: 'Home / X', url: 'https://x.com/home' }], profiles: [{ id: 'default', name: 'Main' }], activeProfile: 'default', }; test('palette: the Deck group only navigates', () => { const none = buildItems({ ...base, decks: [] }, '').filter(item => item.group === 'Deck'); assert.deepEqual(none.map(item => item.action), [{ type: 'deck' }]); const context: PaletteContext = { ...base, decks: [ { id: 'd1', name: 'Monitor', columns: [{ id: 'c1', title: 'Home', profile: 'default' }, { id: 'c2', title: 'Mentions', profile: 'default' }] }, { id: 'd2', name: 'Launch', columns: [] }, ], activeDeck: 'd1', }; const deck = buildItems(context, '').filter(item => item.group === 'Deck'); assert.deepEqual(deck.map(item => item.action.type), ['deck', 'deck-add', 'deck-focus', 'deck-focus', 'deck-new']); assert.deepEqual(deck[0]!.action, { type: 'deck', deckId: 'd2' }, 'switch to the other deck'); assert.deepEqual(deck[2]!.action, { type: 'deck-focus', deckId: 'd1', columnId: 'c1' }); assert.equal(deck[2]!.hint, 'Alt+1'); for (const item of deck) assert.ok(['deck', 'deck-add', 'deck-focus', 'deck-new'].includes(item.action.type), 'navigation only'); const found = buildItems(context, 'mentions'); assert.ok(found.some(item => item.id === 'deck-focus:c2'), 'columns are searchable'); const power = buildItems(context, 'tweetdeck'); assert.ok(power.some(item => item.group === 'Deck')); });