Ctrl K
ComunidadGuías…

test/client-mobile-view.test.js

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

Archivo original ↗Preguntar a KiBot
// @vitest-environment happy-dom
import { describe, it, expect, vi, beforeEach } from 'vitest';

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

import { agents, repos, orphans, setActiveSession, setView } from '../public/modules/state.js';
import { showJobBoard, hideJobBoard } from '../public/modules/jobs.js';
import { handleFileDiff, closeDiffViewer } from '../public/modules/explorer.js';
import { switchToSession } from '../public/modules/terminal.js';

// Below 700px style.css shows one panel, keyed on body[data-view]; these are
// the state transitions behind it. The nav itself is plain markup + CSS.
const NAV_HTML = `
  <nav class="mobile-nav" id="mobile-nav">
    <button data-view="files">Files</button>
    <button data-view="office" aria-current="true">Office</button>
    <button data-view="terminal">Terminal</button>
  </nav>`;
const PANELS_HTML = `
  <div id="explorer-content"></div>
  <div class="spawn-form" id="spawn-form" style="display:none"></div>
  <canvas id="office-canvas"></canvas>
  <div id="office-empty"></div>
  <div id="diff-viewer" style="display:none">
    <span id="diff-viewer-path"></span><button id="diff-viewer-close"></button>
    <pre id="diff-viewer-content"></pre>
  </div>
  <div id="terminal-tabs"></div>
  <div id="terminal-empty"></div>
  <div id="job-board" style="display:none"></div>`;

const current = () => [...document.querySelectorAll('.mobile-nav button')]
  .filter(b => b.getAttribute('aria-current') === 'true').map(b => b.dataset.view);

beforeEach(() => {
  document.body.innerHTML = NAV_HTML + PANELS_HTML;
  document.body.dataset.view = 'office';
  agents.clear();
  repos.clear();
  orphans.clear();
  setActiveSession(null);
  closeDiffViewer();
  hideJobBoard();
});

describe('setView', () => {
  it('sets body[data-view] and moves aria-current to the matching nav button', () => {
    setView('files');
    expect(document.body.dataset.view).toBe('files');
    expect(current()).toEqual(['files']);
    setView('terminal');
    expect(document.body.dataset.view).toBe('terminal');
    expect(current()).toEqual(['terminal']);
  });

  it('is harmless without the nav in the DOM (desktop-only fixtures)', () => {
    document.body.innerHTML = PANELS_HTML;
    expect(() => setView('terminal')).not.toThrow();
    expect(document.body.dataset.view).toBe('terminal');
  });

  it('an unknown view still lands on the body and clears every nav button', () => {
    setView('nope');
    expect(document.body.dataset.view).toBe('nope');
    expect(current()).toEqual([]);
  });
});

describe('panel changes pick the phone view', () => {
  it('opening a file diff lands on the office panel, closing it returns to files', () => {
    setView('files');
    handleFileDiff({ sessionId: 's1', filePath: 'src/a.js', diff: '@@ -1 +1 @@\n-a\n+b' });
    expect(document.body.dataset.view).toBe('office');
    expect(current()).toEqual(['office']);
    expect(document.getElementById('diff-viewer').style.display).toBe('flex');
    closeDiffViewer();
    expect(document.body.dataset.view).toBe('files');
    closeDiffViewer(); // already closed: a state-change repaint must not move the view
    setView('office');
    closeDiffViewer();
    expect(document.body.dataset.view).toBe('office');
  });

  // Spawns, exits and the reconnect replay all switch sessions or open the
  // board; only a tap on the office canvas moves a phone to the terminal.
  it('switching sessions or opening the board in the background leaves the view alone', () => {
    const termEl = document.createElement('div');
    document.body.appendChild(termEl);
    agents.set('s1', { name: 'Viper', state: 'IDLE', termEl, term: { scrollToBottom() {}, focus() {} } });
    setView('files');
    switchToSession('s1');
    expect(termEl.style.display).toBe('block');
    expect(document.body.dataset.view).toBe('files');
    showJobBoard();
    expect(document.getElementById('job-board').style.display).toBe('flex');
    expect(document.body.dataset.view).toBe('files');
  });
});