import { describe, expect, it, vi } from 'vitest' import type { FitAddon } from '@xterm/addon-fit' import type { Terminal } from '@xterm/xterm' import { isTerminalDisposed, markTerminalDisposed, safeTerminalDispose, safeTerminalFit, safeTerminalWrite, } from '../../src/utils/safeTerminalFit' describe('safeTerminalFit', () => { it('returns false without an addon', () => { expect(safeTerminalFit(null)).toBe(false) }) it('skips fit when terminal element is detached', () => { const fit = vi.fn() const term = { element: { isConnected: false, parentElement: { clientWidth: 100, clientHeight: 40 } }, } as unknown as Terminal expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false) expect(fit).not.toHaveBeenCalled() }) it('skips fit when host has zero size', () => { const fit = vi.fn() const term = { element: { isConnected: true, parentElement: { clientWidth: 0, clientHeight: 0 } }, } as unknown as Terminal expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false) expect(fit).not.toHaveBeenCalled() }) it('swallows renderer dimension races', () => { const fit = vi.fn(() => { throw new TypeError("undefined is not an object (evaluating 'this._renderer.value.dimensions')") }) const term = { element: { isConnected: true, parentElement: { clientWidth: 80, clientHeight: 40 } }, } as unknown as Terminal expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false) expect(fit).toHaveBeenCalledOnce() }) it('returns true on successful fit', () => { const fit = vi.fn() const term = { element: { isConnected: true, parentElement: { clientWidth: 80, clientHeight: 40 } }, } as unknown as Terminal expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(true) }) it('blocks write after mark disposed', () => { const write = vi.fn() const term = { write } as unknown as Terminal markTerminalDisposed(term) expect(isTerminalDisposed(term)).toBe(true) safeTerminalWrite(term, 'x') expect(write).not.toHaveBeenCalled() }) it('marks disposed and removes element before dispose', () => { const raf: FrameRequestCallback[] = [] const original = globalThis.requestAnimationFrame globalThis.requestAnimationFrame = ((cb: FrameRequestCallback) => { raf.push(cb) return raf.length }) as typeof requestAnimationFrame const dispose = vi.fn() const remove = vi.fn() const term = { dispose, element: { remove }, } as unknown as Terminal safeTerminalDispose(term) expect(isTerminalDisposed(term)).toBe(true) expect(remove).toHaveBeenCalledOnce() expect(dispose).not.toHaveBeenCalled() // Drain double-rAF queue used by safeTerminalDispose. while (raf.length) { const next = raf.shift()! next(0) } expect(dispose).toHaveBeenCalledOnce() globalThis.requestAnimationFrame = original }) })