Ctrl K
ComunidadGuías…

tests/selectedNotesContext.test.mjs

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

Archivo original ↗Preguntar a KiBot
import assert from 'node:assert/strict';
import test from 'node:test';
import { readFileSync } from 'node:fs';
import vm from 'node:vm';

const chatSource = readFileSync(new URL('../static/js/chat.js', import.meta.url), 'utf8');
const sessionSource = readFileSync(new URL('../static/js/sessions.js', import.meta.url), 'utf8');
const notesSource = readFileSync(new URL('../static/js/features/notes/application/selection.js', import.meta.url), 'utf8');

function setup() {
  const start = chatSource.indexOf("  window.addEventListener('odysseus:notes-context-selected'");
  const end = chatSource.indexOf('  function _fmtContextNumber(n)', start);
  assert.ok(start !== -1 && end > start, 'production event handlers must exist');
  const handlers = new Map();
  const rendered = [];
  let sessionId = null;
  let pending = false;
  let clear;
  const win = { addEventListener: (name, fn) => handlers.set(name, fn) };
  const doc = {
    getElementById: () => ({ addEventListener: (name, fn) => { if (name === 'click') clear = fn; } }),
  };
  const sessionModule = {
    getCurrentSessionId: () => sessionId,
    hasPendingChat: () => pending,
  };
  vm.runInNewContext(
    `const _selectedNoteIdsBySession = new Map(); let _pendingSelectedNoteIds = [];
${chatSource.slice(start, end)}`,
    { window: win, document: doc, sessionModule, console, _renderSelectedNotesIndicator: ids => rendered.push(Array.from(ids)) },
  );
  return {
    setSession(id, isPending = false) { sessionId = id; pending = isPending; },
    emit(name, detail) { handlers.get(name)({ detail }); },
    selected(ids) { const detail = { noteIds: ids, accepted: false }; handlers.get('odysseus:notes-context-selected')({ detail }); return detail; },
    clear() { clear(); },
    lastRendered() { return rendered.at(-1); },
  };
}

test('pending selections transfer only to the materialized chat', () => {
  const ctx = setup();
  ctx.setSession(null, true);
  assert.equal(ctx.selected(['a', 'b']).accepted, true);
  assert.deepEqual(ctx.lastRendered(), ['a', 'b']);
  ctx.setSession('new-chat');
  ctx.emit('odysseus:session-changed', { sessionId: 'new-chat', materialized: true });
  assert.deepEqual(ctx.lastRendered(), ['a', 'b']);
  ctx.setSession('other-chat');
  ctx.emit('odysseus:session-changed', { sessionId: 'other-chat' });
  assert.deepEqual(ctx.lastRendered(), []);
  ctx.setSession('new-chat');
  ctx.emit('odysseus:session-changed', { sessionId: 'new-chat' });
  assert.deepEqual(ctx.lastRendered(), ['a', 'b']);
  ctx.clear();
  assert.deepEqual(ctx.lastRendered(), []);
});

test('new chat resets old indicator and invalid selection is rejected', () => {
  const ctx = setup();
  assert.equal(ctx.selected(['a']).accepted, false);
  ctx.setSession('old-chat');
  assert.equal(ctx.selected(['a']).accepted, true);
  ctx.setSession(null, true);
  ctx.emit('odysseus:session-changed', { sessionId: null });
  assert.deepEqual(ctx.lastRendered(), []);
  assert.equal(ctx.selected(['b']).accepted, true);
  ctx.emit('odysseus:session-changed', { sessionId: null });
  assert.deepEqual(ctx.lastRendered(), []);
  ctx.setSession('fresh');
  ctx.emit('odysseus:session-changed', { sessionId: 'fresh', materialized: true });
  assert.deepEqual(ctx.lastRendered(), []);
});

test('producer events and rejection acknowledgement are wired in source', () => {
  const create = sessionSource.slice(sessionSource.indexOf('export function createDirectChat'), sessionSource.indexOf('export async function materializePendingSession'));
  const materialize = sessionSource.slice(sessionSource.indexOf('export async function materializePendingSession'), sessionSource.indexOf('export function preMaterializePendingSession'));
  assert.match(create, /currentSessionId = null;[\s\S]{0,180}odysseus:session-changed/);
  assert.match(materialize, /currentSessionId = payload.id;[\s\S]{0,180}odysseus:session-changed/);
  assert.match(notesSource, /if \(!detail\.accepted\)/);
});