mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-25 16:19:39 +00:00
Some checks failed
CI / semgrep (push) Has been cancelled
Tests / test-platforms (windows-latest) (push) Has been cancelled
Tests / desktop (push) Has been cancelled
Tests / test (22) (push) Has been cancelled
Tests / test (22.13.0) (push) Has been cancelled
Tests / test (24) (push) Has been cancelled
Tests / test (26) (push) Has been cancelled
Tests / test-platforms (macos-latest) (push) Has been cancelled
108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import { PassThrough } from 'node:stream'
|
|
|
|
import React from 'react'
|
|
import { render } from 'ink'
|
|
import stripAnsi from 'strip-ansi'
|
|
import { afterEach, beforeEach, describe, expect, it, onTestFinished, vi } from 'vitest'
|
|
|
|
import { InteractiveDashboard } from '../src/dashboard.js'
|
|
import type { ProjectSummary } from '../src/types.js'
|
|
|
|
const MOUSE_TRACKING_ON = '\x1b[?1000h\x1b[?1006h'
|
|
const MOUSE_TRACKING_OFF = '\x1b[?1006l\x1b[?1000l'
|
|
|
|
const PROJECTS: ProjectSummary[] = [
|
|
{ project: 'proj', projectPath: 'proj', sessions: [], totalCostUSD: 0, totalSavingsUSD: 0, totalApiCalls: 0 },
|
|
]
|
|
|
|
// The viewport writes the tracking sequences to the process's own stdout (the
|
|
// terminal), not to Ink's injected stream, and only when both ends are a TTY.
|
|
let writes: string[]
|
|
let stdoutTTY: boolean | undefined
|
|
let stdinTTY: boolean | undefined
|
|
|
|
beforeEach(() => {
|
|
writes = []
|
|
stdoutTTY = process.stdout.isTTY
|
|
stdinTTY = process.stdin.isTTY
|
|
process.stdout.isTTY = true
|
|
process.stdin.isTTY = true
|
|
vi.spyOn(process.stdout, 'write').mockImplementation(chunk => { writes.push(String(chunk)); return true })
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
process.stdout.isTTY = stdoutTTY as boolean
|
|
process.stdin.isTTY = stdinTTY as boolean
|
|
})
|
|
|
|
function makeTui(columns = 120) {
|
|
const stdin = new PassThrough() as PassThrough & NodeJS.ReadStream
|
|
const stdout = new PassThrough({ highWaterMark: 4 * 1024 * 1024 }) as PassThrough & NodeJS.WriteStream
|
|
stdin.isTTY = true
|
|
stdin.setRawMode = () => stdin
|
|
stdin.ref = () => stdin
|
|
stdin.unref = () => stdin
|
|
stdout.isTTY = true
|
|
stdout.columns = columns
|
|
stdout.rows = 50
|
|
return { stdin, stdout }
|
|
}
|
|
|
|
async function mountDashboard(columns = 120) {
|
|
const { stdin, stdout } = makeTui(columns)
|
|
const frames: string[] = []
|
|
stdout.on('data', chunk => frames.push(stripAnsi(String(chunk))))
|
|
const app = render(React.createElement(InteractiveDashboard, {
|
|
initialProjects: PROJECTS,
|
|
initialPeriod: 'today',
|
|
initialProvider: 'all',
|
|
refreshSeconds: 0,
|
|
windowColumns: columns,
|
|
}), { stdin, stdout, debug: true, interactive: true, patchConsole: false })
|
|
await app.waitUntilRenderFlush()
|
|
return { app, stdin, frames }
|
|
}
|
|
|
|
describe('dashboard mouse tracking toggle (#951)', () => {
|
|
it('does not enable mouse tracking at mount, so text selection keeps working', async () => {
|
|
const { app } = await mountDashboard()
|
|
onTestFinished(() => app.unmount())
|
|
|
|
expect(writes.join('')).not.toContain(MOUSE_TRACKING_ON)
|
|
})
|
|
|
|
it('m enables tracking, and m again disables it without waiting for unmount', async () => {
|
|
const { app, stdin } = await mountDashboard()
|
|
onTestFinished(() => app.unmount())
|
|
|
|
stdin.write('m')
|
|
await app.waitUntilRenderFlush()
|
|
expect(writes.join('')).toContain(MOUSE_TRACKING_ON)
|
|
|
|
writes.length = 0
|
|
stdin.write('m')
|
|
await app.waitUntilRenderFlush()
|
|
expect(writes.join('')).toContain(MOUSE_TRACKING_OFF)
|
|
expect(writes.join('')).not.toContain(MOUSE_TRACKING_ON)
|
|
})
|
|
|
|
it('unmount after enabling leaves tracking off', async () => {
|
|
const { app, stdin } = await mountDashboard()
|
|
|
|
stdin.write('m')
|
|
await app.waitUntilRenderFlush()
|
|
writes.length = 0
|
|
app.unmount()
|
|
await app.waitUntilExit()
|
|
|
|
expect(writes.join('')).toContain(MOUSE_TRACKING_OFF)
|
|
})
|
|
|
|
it('shows the toggle in the key hints at 80 columns', async () => {
|
|
const { app, frames } = await mountDashboard(80)
|
|
onTestFinished(() => app.unmount())
|
|
|
|
expect(frames.join('')).toContain('m mouse')
|
|
})
|
|
})
|