Ctrl K
ComunidadGuías…

test/client-billion-explorer.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// @vitest-environment happy-dom
// Billion's row in the explorer: pinned above the repos, with a Start button
// while it is stopped, and absent on a server that does not run it.
import { describe, it, expect, vi, beforeEach } from 'vitest';

vi.mock('../public/modules/ws.js', () => ({ send: vi.fn(() => true) }));
vi.mock('../public/modules/terminal.js', () => ({ switchToSession: vi.fn(), restartAgent: vi.fn(), restartPending: () => false }));

import { send } from '../public/modules/ws.js';
import { switchToSession } from '../public/modules/terminal.js';
import { agents, repos, orphans, setActiveSession, setBillionEnabled, billionFirst } from '../public/modules/state.js';
import { renderExplorer } from '../public/modules/explorer.js';

const row = () => document.querySelector('.explorer-billion');

beforeEach(() => {
  document.body.innerHTML = '<div id="explorer-content"></div>';
  agents.clear();
  repos.clear();
  orphans.clear();
  setActiveSession(null);
  setBillionEnabled(true);
  vi.clearAllMocks();
});

describe('Billion in the explorer', () => {
  it('offers Start while no Billion is running, and sends billion-start without switching', () => {
    renderExplorer();
    expect(row().textContent).toContain('stopped');
    row().querySelector('.explorer-billion-start').click();
    expect(send).toHaveBeenCalledWith({ type: 'billion-start' });
    expect(switchToSession).not.toHaveBeenCalled();
  });

  it('shows a running Billion without Start, first, and not again under "no repo"', () => {
    repos.set('/r/app', { slug: 'app', exists: true });
    agents.set('b', { name: 'Billion', isBillion: true, state: 'WAITING', repoPath: null });
    setActiveSession('b');
    renderExplorer();
    const content = document.getElementById('explorer-content');
    expect(content.firstElementChild).toBe(row());
    expect(row().querySelector('.explorer-billion-start')).toBeNull();
    expect(row().querySelector('.explorer-branch').classList.contains('active')).toBe(true);
    expect(content.querySelectorAll('.explorer-repo')).toHaveLength(2);   // Billion + app, no legacy section
    row().querySelector('.explorer-branch').click();
    expect(switchToSession).toHaveBeenCalledWith('b');
  });

  it('starts a stopped Billion from anywhere on its row, once', () => {
    renderExplorer();
    row().querySelector('.explorer-branch').click();
    expect(send).toHaveBeenCalledTimes(1);
    expect(send).toHaveBeenCalledWith({ type: 'billion-start' });
    send.mockClear();
    row().querySelector('.explorer-billion-start').click();   // the button does not also fire the row
    expect(send).toHaveBeenCalledTimes(1);
  });

  it('shows red only for a Billion that ran and stopped, grey before it has run', () => {
    renderExplorer();
    expect(row().querySelector('.explorer-dot').style.background).toBe('var(--state-idle)');
    agents.set('b', { name: 'Billion', isBillion: true, state: 'DISCONNECTED' });
    renderExplorer();
    expect(row().querySelector('.explorer-dot').style.background).toBe('var(--state-disconnected)');
  });

  it('treats a disconnected Billion as stopped', () => {
    agents.set('b', { name: 'Billion', isBillion: true, state: 'DISCONNECTED' });
    renderExplorer();
    expect(row().querySelector('.explorer-billion-start')).not.toBeNull();
  });

  it('still says "No repos yet" when Billion is the only agent', () => {
    agents.set('b', { name: 'Billion', isBillion: true, state: 'WAITING' });
    renderExplorer();
    expect(document.querySelector('.explorer-empty').textContent).toBe('No repos yet. Add one with + above.');
  });

  it('has no row on a server without Billion', () => {
    setBillionEnabled(false);
    renderExplorer();
    expect(row()).toBeNull();
  });
});

describe('billionFirst', () => {
  it('puts Billion first and keeps everyone else in order', () => {
    const m = new Map([['a', {}], ['b', { isBillion: true }], ['c', {}]]);
    expect(billionFirst(m).map(([id]) => id)).toEqual(['b', 'a', 'c']);
  });

  it('shows the CLI Billion runs on, and switches to the other once confirmed', () => {
    agents.set('b', { name: 'Billion', isBillion: true, state: 'WAITING', repoPath: null, agent: 'claude' });
    renderExplorer();
    const cli = row().querySelector('.explorer-billion-cli');
    expect(cli.textContent).toBe('claude');
    globalThis.confirm = vi.fn(() => false);
    cli.click();
    expect(send).not.toHaveBeenCalled();
    globalThis.confirm = vi.fn(() => true);
    cli.click();
    expect(send).toHaveBeenCalledWith({ type: 'billion-switch', agent: 'codex' });
    expect(switchToSession).not.toHaveBeenCalled();
  });
});