import test from 'brittle' import { readFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { attachBareOsTuiSdk } from '../../bare-os-booter/lib/services/bare-os-tui-sdk.js' const __dirname = dirname(fileURLToPath(import.meta.url)) async function load() { const parts = await Promise.all([ readFile(join(__dirname, '../lib/irc/irc-parse.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-isupport.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-sasl.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-state.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-config.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-client.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-dial.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-commands.js'), 'utf8'), readFile(join(__dirname, '../lib/irc/irc-tui.js'), 'utf8') ]) const hook = {} new Function( 'exports', parts.join('\n') + '\nexports.bareIrcCreateClient = bareIrcCreateClient;\nexports.bareIrcCreateTuiApp = bareIrcCreateTuiApp;\n' )(hook) return hook } function makeStream(isTTY) { return { isTTY: !!isTTY, columns: 80, rows: 24, write() {}, setRawMode() {}, resume() {}, pause() {}, on() {}, removeListener() {} } } function makeCtx() { const ctx = { env: {}, console: { error() {}, log() {} }, replStdin: makeStream(true), replStdout: makeStream(true), suspendReplForSubprocess() {}, resumeReplAfterSubprocess() {} } attachBareOsTuiSdk(ctx) return ctx } test('irc TUI paints a fixed-height frame', async (t) => { const h = await load() const ctx = makeCtx() const client = h.bareIrcCreateClient({ nick: 'alice', network: 'libera' }) client.state.connection = 'registered' client.state.registered = true client.state.nick = 'alice' client.handleMessage({ tags: {}, source: 'bob!b@h', nick: 'bob', user: 'b', host: 'h', command: 'PRIVMSG', params: ['#bareos', 'hello from test'] }) const chan = Object.values(client.state.buffers).find( (b) => b.kind === 'channel' ) t.ok(chan) client.state.currentId = chan.id const app = h.bareIrcCreateTuiApp(ctx, client) app.width = 80 app.height = 24 app._sync() const view = ctx.tui.style.stripAnsi(app.view()) t.is(view.split('\n').length, 24) t.ok( view.includes('libera') || view.includes('#bareos') || view.includes('alice') ) t.ok(view.includes('hello from test')) }) test('irc TUI keeps nicks in a right-hand column', async (t) => { const h = await load() const ctx = makeCtx() const client = h.bareIrcCreateClient({ nick: 'alice', network: 'libera' }) client.state.registered = true client.handleMessage({ command: 'JOIN', nick: 'alice', params: ['#ubuntu'] }) client.handleMessage({ command: '353', params: ['alice', '=', '#ubuntu', '@op +voice nickonly'] }) client.handleMessage({ command: 'PRIVMSG', nick: 'op', source: 'op!a@b', params: ['#ubuntu', 'hello room'] }) const app = h.bareIrcCreateTuiApp(ctx, client) app.width = 120 app.height = 24 app.roomsOpen = false app._sync() const view = ctx.tui.style.stripAnsi(app.view()) const lines = view.split('\n') t.ok(view.includes('hello room')) t.ok(view.includes('@op') || view.includes('op')) const chatLine = lines.find((l) => l.includes('hello room')) t.ok(chatLine) const bar = chatLine.lastIndexOf('\u2502') t.ok(bar > chatLine.indexOf('hello room')) const nickLine = lines.find((l) => l.includes('\u2502') && /@op/.test(l)) t.ok(nickLine) t.ok(nickLine.lastIndexOf('\u2502') < nickLine.indexOf('@op')) }) test('irc TUI rooms browser lists featured channels', async (t) => { const h = await load() const ctx = makeCtx() const client = h.bareIrcCreateClient({ nick: 'alice' }) const app = h.bareIrcCreateTuiApp(ctx, client) app.width = 80 app.height = 24 app._openRooms('') app._sync() const view = ctx.tui.style.stripAnsi(app.view()) t.ok(app.roomsOpen) t.ok(view.includes('#libera')) }) test('irc TUI help overlay', async (t) => { const h = await load() const ctx = makeCtx() const client = h.bareIrcCreateClient({ nick: 'alice' }) const app = h.bareIrcCreateTuiApp(ctx, client) app.update(ctx.tui.decode('?')[0]) t.ok(app.help) const ov = app.overlay({ width: 80, height: 24 }) t.ok(ov && ctx.tui.style.stripAnsi(ov.text).includes('Libera')) }) test('kernel irc bin includes TEA factory after build', async (t) => { const bin = await readFile(join(__dirname, '../../../kernel/bin/irc'), 'utf8') t.ok(bin.includes('bareIrcCreateTuiApp')) t.ok(bin.includes('ctx.tui.run')) t.ok(bin.includes('irc.libera.chat')) })