Ctrl K
ComunidadGuías…

test/clay-split-view-screen.test.js

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

Archivo original ↗Preguntar a KiBot
var test = require('node:test'), assert = require('node:assert/strict'), fs = require('node:fs'), vm = require('node:vm');
function load(context, name) { vm.runInContext(fs.readFileSync('lib/public/modules/' + name + '.js', 'utf8').replace(/^import .*;\n/gm, '').replace(/^export /gm, ''), context); }
function fixture() {
  var values = { connected: true, myUserId: 'owner', currentSlug: 'alpha', projectSplitViewActiveId: 'view', projectSplitViewFocusedOrigin: null, activeSessionId: 1,
    paneBindingSessions: [{ id: 1, sessionOriginId: 'a', ownerId: 'owner' }, { id: 2, sessionOriginId: 'b', ownerId: 'owner' }],
    projectSplitViews: [{ id: 'view', revision: 2, tree: { axis: 'horizontal', ratio: 0.5, left: { id: 'a' }, right: { id: 'b' } }, sessions: [{ id: 1, sessionOriginId: 'a' }, { id: 2, sessionOriginId: 'b' }] }] };
  var panels = {};
  var sent = [], callbacks = new Map(), timers = [], sequence = 0, handlers = {}, applied = 0;
  var socket = { readyState: 1, send: function (text) { sent.push(JSON.parse(text)); } };
  var frames = values.paneBindingSessions.map(function (session) {
    var frame = { dataset: { sessionOriginId: session.sessionOriginId, sessionId: String(session.id) }, contentDocument: {}, contentWindow: {}, shown: true, cached: false };
    var binding = { projectSlug: 'alpha', viewerId: 'owner', ownerId: 'owner', sessionId: session.id, sessionOriginId: session.sessionOriginId, role: 'driver', workerGeneration: null, parentSessionOriginId: null, groupStamp: null, requestId: 'binding-' + session.id, generation: 1 };
    var live = { ready: true, projectSlug: 'alpha', ownerId: 'owner', sessionId: session.id, binding: binding };
    frame.closest = function () { return frame.cached; };
    frame.live = live; frame.clayPane = { source: frame.contentWindow, document: frame.contentDocument, identity: function () { return live; } }; return frame;
  });
  var context = vm.createContext({ store: { snap: function () { return values; }, get: function (key) { return values[key]; } }, getWs: function () { return socket; },
    paneOwner: function (pane) { return pane.kind === 'file' ? 'file-' + pane.instanceId : pane.kind; }, filePaneState: function (id) { return (values.filePaneInstances || {})[id]; },
    document: { getElementById: function (id) { return panels[id]; }, hidden: false, querySelectorAll: function () { return frames; }, addEventListener: function (name, callback) { handlers[name] = callback; }, removeEventListener: function (name) { delete handlers[name]; } },
    visible: function (frame) { return frame.shown; }, applyReviewedSplitView: function () { applied++; return true; },
    requestAnimationFrame: function (callback) { var id = ++sequence; callbacks.set(id, callback); return id; }, cancelAnimationFrame: function (id) { callbacks.delete(id); },
    setTimeout: function (callback) { timers.push(callback); return timers.length; }, clearTimeout: function () {} });
  load(context, 'pane-binding'); load(context, 'clay-tool-pane-screen'); load(context, 'clay-split-view-screen');
  function tick() { var pending = Array.from(callbacks.values()); callbacks.clear(); pending.forEach(function (callback) { callback(); }); }
  return { panels: panels, api: context, values: values, frames: frames, socket: socket, sent: sent, tick: tick, timers: timers, handlers: handlers, applied: function () { return applied; } };
}
test('screen probe reports actual live bindings separately from hidden and cached panes after two stable frames', function () {
  var f = fixture(); f.frames[1].cached = true; f.frames[1].shown = false;
  f.api.handleSplitViewProbe({ type: 'clay_view_probe', probeId: 'probe' }); f.tick(); assert.equal(f.sent.length, 0); f.tick();
  var snap = f.sent[0].snapshot; assert.equal(snap.panes[0].visible, true); assert.equal(snap.panes[0].ready, true);
  assert.equal(snap.panes[1].visible, false); assert.equal(snap.panes[1].cached, true); assert.equal(snap.panes[1].ready, false);
});
test('render acknowledgement waits for late binding readiness and two identical binding generations', function () {
  var f = fixture(), view = f.values.projectSplitViews[0]; f.frames[1].live.ready = false;
  f.api.observeSplitViewRendering({ deliveryId: 'd', renderToken: 'token', renderDeadline: Date.now() + 5000, target: { view: view, focusOrigin: null } }, f.socket);
  f.tick(); f.tick(); assert.equal(f.sent.length, 0);
  f.frames[1].live.ready = true; f.tick(); f.frames[1].live.binding.generation++; f.tick(); assert.equal(f.sent.length, 0); f.tick();
  assert.equal(f.sent.length, 1); assert.equal(f.sent[0].visible, true); assert.equal(f.sent[0].snapshot.panes[1].binding.generation, 2);
  f.timers[0](); assert.equal(f.sent.length, 1);
});
test('replaced documents, cached frames, Stop and timeout cannot claim rendered success', function () {
  for (var mode of ['document', 'cached', 'stop', 'timeout']) {
    var f = fixture();
    if (mode === 'document') f.frames[0].contentDocument = {};
    if (mode === 'cached') f.frames[0].cached = true;
    f.api.observeSplitViewRendering({ deliveryId: 'd', renderToken: 'token', renderDeadline: Date.now() + 5000, target: { view: f.values.projectSplitViews[0], focusOrigin: null } }, f.socket);
    if (mode === 'stop') f.handlers.click({ target: { closest: function () { return true; } } });
    else { f.tick(); if (mode !== 'timeout') f.tick(); f.timers[0](); }
    assert.equal(f.sent.length, 1); assert.equal(f.sent[0].visible, false, mode);
  }
});
test('changed contextual screen refuses reviewed open before invoking the shared human action', function () {
  var f = fixture(), target = { expectedScreen: f.api.splitViewScreenSnapshot(), view: f.values.projectSplitViews[0] };
  f.values.projectSplitViewFocusedOrigin = 'a'; assert.equal(f.api.applySplitViewNavigation(target), false); assert.equal(f.applied(), 0);
  f.values.projectSplitViewFocusedOrigin = null; assert.equal(f.api.applySplitViewNavigation(target), true); assert.equal(f.applied(), 1);
});

test('server render proof rejects hidden, cached, wrong active session and changed layout evidence', function () {
  var f = fixture(), proof = require('../lib/clay-split-view-render-proof'), view = f.values.projectSplitViews[0];
  view.activeSessionOriginId = 'a';
  var target = { projectSlug: 'alpha', view: view, focusOrigin: null, expectedScreen: { ownerId: 'owner' } };
  var current = { ws: f.socket, slug: 'alpha', sessionId: 1 };
  var snapshot = JSON.parse(JSON.stringify(f.api.splitViewScreenSnapshot()));
  assert.doesNotThrow(function () { proof.validate(target, { snapshot: snapshot, visible: true, surface: 'split_view' }, current, f.socket); });
  for (var mode of ['hidden', 'cached', 'binding', 'active', 'layout', 'owner']) {
    var changed = JSON.parse(JSON.stringify(snapshot));
    if (mode === 'hidden') changed.hidden = true;
    if (mode === 'cached') changed.panes[0].cached = true;
    if (mode === 'binding') changed.panes[0].binding.sessionOriginId = 'replaced';
    if (mode === 'active') changed.activeSessionId = 2;
    if (mode === 'layout') changed.tree.ratio = 0.7;
    if (mode === 'owner') changed.ownerId = 'foreign';
    assert.throws(function () { proof.validate(target, { snapshot: changed, visible: true, surface: 'split_view' }, current, f.socket); }, undefined, mode);
  }
});


test('canonical browser fixture imports the production project module graph', function () {
  var source = fs.readFileSync('test/fixtures/split-view-canonical-browser.js', 'utf8');
  for (var name of ['store', 'contextual-clay', 'split-view-layout-client', 'sidebar-sessions']) assert.ok(source.includes("from '/p/reuse/modules/" + name + ".js'"));
  assert.doesNotMatch(source, /from ['"]\/modules\//);
});

test('empty, single-session, tool-only and mixed observations preserve exact tool identities', function () {
  for (var mode of ['empty', 'single', 'tools', 'mixed']) {
    var f = fixture(), view = f.values.projectSplitViews[0];
    view.sessions = mode === 'single' || mode === 'mixed' ? [view.sessions[0]] : []; f.frames.splice(view.sessions.length);
    view.tree = view.sessions.length ? { id: 'a' } : null; view.activeSessionOriginId = view.sessions.length ? 'a' : null;
    view.workspace = { version: 1, panes: [], tree: null, activePaneId: null };
    if (mode === 'tools' || mode === 'mixed') {
      view.workspace.panes.push({ instanceId: 'file-a', kind: 'file', open: true, placement: 'docked', resource: { path: 'a.md', mode: 'pin', rendered: true } });
      f.values.filePaneInstances = { 'file-a': { content: 'alpha', resource: { path: 'a.md', mode: 'pin', rendered: true } } };
      f.panels['file-file-a'] = { dataset: { workbenchPlacement: 'docked' }, shown: true, getBoundingClientRect: function () { return { x: 1, y: 2, width: 300, height: 400 }; } };
    }
    var snap = f.api.splitViewScreenSnapshot(); assert.equal(snap.tools.length, mode === 'tools' || mode === 'mixed' ? 1 : 0);
    var proof = require('../lib/clay-split-view-render-proof');
    assert.doesNotThrow(function () { proof.validate({ projectSlug: 'alpha', view: view, focusOrigin: null, expectedScreen: { ownerId: 'owner' } }, { snapshot: snap, visible: true, surface: 'split_view' }, { ws: f.socket, slug: 'alpha', sessionId: 1 }, f.socket); });
    f.api.observeSplitViewRendering({ deliveryId: mode, renderToken: 'token', renderDeadline: Date.now() + 5000, target: { view: view, focusOrigin: null } }, f.socket); f.tick(); f.tick(); assert.equal(f.sent[0].visible, true, mode);
  }
});
test('unavailable tools cannot claim readiness or rendered success and malformed geometry is rejected', function () {
  var f = fixture(), view = f.values.projectSplitViews[0];
  view.workspace = { panes: [{ instanceId: 'file-a', kind: 'file', open: true, resource: { path: 'a.md', mode: 'pin', rendered: true } }] };
  f.values.filePaneInstances = { 'file-a': { error: 'Access denied' } };
  f.panels['file-file-a'] = { dataset: { workbenchPlacement: 'docked' }, shown: true, getBoundingClientRect: function () { return { x: 0, y: 0, width: 300, height: 400 }; } };
  var snapshot = JSON.parse(JSON.stringify(f.api.splitViewScreenSnapshot())), proof = require('../lib/clay-split-view-render-proof');
  assert.equal(snapshot.tools[0].unavailable, true); assert.equal(snapshot.tools[0].ready, false);
  assert.throws(function () { proof.validate({ projectSlug: 'alpha', view: view, focusOrigin: null, expectedScreen: { ownerId: 'owner' } }, { snapshot: snapshot, visible: true, surface: 'split_view' }, { ws: f.socket, slug: 'alpha', sessionId: 1 }, f.socket); });
  snapshot.tools[0].geometry.width = 100001; assert.equal(proof.snapshotSchema.safeParse(snapshot).success, false);
  snapshot.tools[0].geometry.width = 300; snapshot.tools.push(snapshot.tools[0]); assert.equal(proof.snapshotSchema.safeParse(snapshot).success, false);
});

test('expanded tool render evidence matches the exact persisted presentation and excludes obscured sessions',function(){
  var f=fixture(),view=f.values.projectSplitViews[0];view.activeSessionOriginId='a';
  view.workspace={version:1,panes:[{instanceId:'file-a',kind:'file',open:true,placement:'docked',resource:{path:'a.md',mode:'pin',rendered:true}}],presentation:{focusedSessionOriginId:null,expandedPaneId:'file-a'}};
  f.frames.forEach(function(frame){frame.shown=false;});f.values.filePaneInstances={'file-a':{content:'alpha',resource:{path:'a.md',mode:'pin',rendered:true}}};
  f.panels['file-file-a']={classList:{contains:function(name){return name==='panel-fullscreen';}},dataset:{workbenchPlacement:'docked'},shown:true,getBoundingClientRect:function(){return{x:7,y:7,width:900,height:700};}};
  var snap=f.api.splitViewScreenSnapshot(),proof=require('../lib/clay-split-view-render-proof');assert.equal(snap.expandedPaneId,'file-a');
  var target={projectSlug:'alpha',view:view,focusOrigin:null,expectedScreen:{ownerId:'owner'}};
  assert.doesNotThrow(function(){proof.validate(target,{snapshot:snap,visible:true,surface:'split_view'},{ws:f.socket,slug:'alpha',sessionId:1},f.socket);});
  snap.expandedPaneId='foreign';assert.equal(proof.snapshotSchema.safeParse(snap).success,false);
});