Ctrl K
ComunidadGuías…

test/ask-user-rendering.test.js

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

Archivo original ↗Preguntar a KiBot
var test = require("node:test");
var assert = require("node:assert/strict");
var path = require("node:path");
var pathToFileURL = require("node:url").pathToFileURL;

function FakeElement(tag) {
  this.tagName = String(tag).toUpperCase();
  this.children = [];
  this.parentNode = null;
  this.dataset = {};
  this.className = "";
  this.listeners = {};
  this.textContent = "";
  this.value = "";
  this.disabled = false;
  var self = this;
  this.classList = {
    add: function () { for (var i = 0; i < arguments.length; i++) if (!self.classList.contains(arguments[i])) self.className += (self.className ? " " : "") + arguments[i]; },
    remove: function (name) { self.className = self.className.split(" ").filter(function (item) { return item && item !== name; }).join(" "); },
    contains: function (name) { return (" " + self.className + " ").indexOf(" " + name + " ") !== -1; },
  };
}

FakeElement.prototype.appendChild = function (child) { child.parentNode = this; this.children.push(child); return child; };
FakeElement.prototype.insertBefore = function (child, before) { var i = this.children.indexOf(before); if (i < 0) return this.appendChild(child); child.parentNode = this; this.children.splice(i, 0, child); return child; };
FakeElement.prototype.addEventListener = function (name, fn) { this.listeners[name] = fn; };
FakeElement.prototype.click = function () { if (this.listeners.click) this.listeners.click({ currentTarget: this }); };
FakeElement.prototype.querySelectorAll = function (selector) {
  var result = [];
  function matches(node) {
    if (selector === ".ask-user-container") return node.classList.contains("ask-user-container");
    if (selector === ".ask-user-question") return node.classList.contains("ask-user-question");
    if (selector === ".ask-user-option") return node.classList.contains("ask-user-option");
    if (selector === ".ask-user-submit") return node.classList.contains("ask-user-submit");
    if (selector === ".ask-user-answer-summary") return node.classList.contains("ask-user-answer-summary");
    if (selector === ".ask-user-other input") return node.tagName === "INPUT" && node.parentNode && node.parentNode.classList.contains("ask-user-other");
    if (selector === ".option-label") return node.classList.contains("option-label");
    return false;
  }
  function visit(node) { if (matches(node)) result.push(node); for (var i = 0; i < node.children.length; i++) visit(node.children[i]); }
  visit(this);
  return result;
};
FakeElement.prototype.querySelector = function (selector) {
  if (selector === ".option-label" && this._innerHTML && !this.querySelectorAll(selector).length) {
    var label = new FakeElement("div"); label.className = "option-label"; this.appendChild(label);
  }
  return this.querySelectorAll(selector)[0] || null;
};
Object.defineProperty(FakeElement.prototype, "innerHTML", { get: function () { return this._innerHTML || ""; }, set: function (value) { this._innerHTML = String(value); } });

test("AskUserQuestion rendering is idempotent per session request and submits once", async function () {
  var originalDocument = global.document;
  var originalWindow = global.window;
  var originalLocalStorage = global.localStorage;
  var originalMarked = global.marked;
  var originalMermaid = global.mermaid;
  var originalPurifier = global.DOMPurify;
  var messages = new FakeElement("div");
  var secondMessages = new FakeElement("div");
  var input = new FakeElement("textarea");
  var sent = [];
  var context;
  global.document = {
    addEventListener: function () {},
    body: new FakeElement("body"),
    createElement: function (tag) { return new FakeElement(tag); },
    querySelectorAll: function (selector) { return messages.querySelectorAll(selector).concat(secondMessages.querySelectorAll(selector)); },
    getElementById: function () { return null; },
  };
  global.window = { addEventListener: function () {}, removeEventListener: function () {} };
  global.localStorage = { getItem: function () { return null; }, setItem: function () {}, removeItem: function () {} };
  global.marked = { use: function () {}, parse: function (value) { return value; } };
  global.mermaid = { initialize: function () {} };
  global.DOMPurify = { sanitize: function (value) { return value; } };
  try {
    var storeModule = await import(pathToFileURL(path.join(__dirname, "../lib/public/modules/store.js")).href);
    storeModule.createStore({ activeSessionId: 101, currentVendor: "claude" });
    var tools = await import(pathToFileURL(path.join(__dirname, "../lib/public/modules/tools.js")).href);
    context = {
      finalizeAssistantBlock: function () {}, stopUrgentBlink: function () {}, setActivity: function () {},
      addToMessages: function (el) { context.messagesEl.appendChild(el); }, scrollToBottom: function () {},
      isMateDm: function () { return false; }, inputEl: input, ws: { send: function (value) { sent.push(JSON.parse(value)); } }, connected: true,
      messagesEl: messages,
    };
    tools.initTools(context);
    tools.resetToolState();
    var question = { questions: [{ question: "Which outcome?", options: [{ label: "Ship" }, { label: "Wait" }] }] };
    tools.renderAskUserQuestion("ask-101", question);
    var first = messages.children[0];
    var freeform = first.querySelector(".ask-user-other input");
    freeform.value = "Keep the current plan";
    freeform.listeners.input();
    var reordered = { questions: [{ options: [{ label: "Ship" }, { label: "Wait" }], question: "Which outcome?" }] };
    tools.renderAskUserQuestion("ask-101", reordered);
    assert.strictEqual(messages.children[0], first);
    assert.equal(messages.querySelectorAll(".ask-user-container").length, 1);
    assert.equal(freeform.value, "Keep the current plan");
    first.querySelector(".ask-user-submit").click();
    first.querySelector(".ask-user-submit").click();
    assert.deepEqual(sent, [{ type: "ask_user_response", toolId: "ask-101", answers: { 0: "Keep the current plan" } }]);

    storeModule.store.set({ activeSessionId: 202 });
    context.messagesEl = secondMessages;
    tools.renderAskUserQuestion("ask-101", question);
    var next = secondMessages.children[0];
    assert.notStrictEqual(next, first);
    assert.equal(messages.querySelectorAll(".ask-user-container").length, 1);
    assert.equal(secondMessages.querySelectorAll(".ask-user-container").length, 1);
    tools.markAskUserAnswered("ask-101", { 0: "Wait" });
    tools.renderAskUserQuestion("ask-101", question);
    assert.equal(next.classList.contains("answered"), true);
    assert.equal(secondMessages.querySelectorAll(".ask-user-container").length, 1);

    storeModule.store.set({ activeSessionId: 303, askUserRequests: {} });
    context.messagesEl = new FakeElement("div");
    tools.renderAskUserQuestion("ask-101", question);
    tools.renderAskUserQuestion("ask-102", question);
    assert.equal(context.messagesEl.querySelectorAll(".ask-user-container").length, 2);
    tools.renderAskUserQuestion('mcp-consent', { source: 'mcp-import', questions: [{ question: 'Import disabled copy?', allowOther: false, options: [{ label: 'Keep unchanged' }, { label: 'Import disabled copy' }] }] });
    var consent = context.messagesEl.querySelectorAll('.ask-user-container').find(function (card) { return card.dataset.toolId === 'mcp-consent'; });
    assert.equal(consent.classList.contains('mcp-import-consent'), true);
    assert.equal(consent.querySelector('.ask-user-other input'), null);
    var beforeConsent = sent.length;
    consent.querySelector('.ask-user-submit').click(); assert.equal(sent.length, beforeConsent);
    consent.querySelectorAll('.ask-user-option')[1].click(); consent.querySelector('.ask-user-submit').click();
    assert.equal(sent[sent.length - 1].toolId, 'mcp-consent'); assert.equal(sent[sent.length - 1].answers[0], 'Import disabled copy');
  } finally {
    global.document = originalDocument;
    global.window = originalWindow;
    global.localStorage = originalLocalStorage;
    global.marked = originalMarked;
    global.mermaid = originalMermaid;
    global.DOMPurify = originalPurifier;
  }
});