Ctrl K
ComunidadGuías…

test/contextual-clay-workspace.test.cjs

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 cp = require('node:child_process');
var path = require('node:path');
var playwright = require('playwright');

test('Open Clay enters the native Mate route with one writable draft and exact review/session; Back restores project', async function (t) {
  var child = cp.fork(path.join(__dirname, 'fixtures/contextual-clay-server.cjs'), [], { stdio: ['ignore', 'pipe', 'pipe', 'ipc'] });
  t.after(function () { child.kill('SIGTERM'); });
  var output = '';
  child.stdout.on('data', function (value) { output += value; }); child.stderr.on('data', function (value) { output += value; });
  var info = await new Promise(function (resolve, reject) {
    var timer = setTimeout(function () { reject(new Error(output)); }, 15000);
    child.once('message', function (info) { clearTimeout(timer); resolve(info); });
  });
  var browser = await playwright.chromium.launch();
  t.after(async function () { await browser.close(); });
  var page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
  page.setDefaultTimeout(8000);
  page.on('pageerror', function (error) { t.diagnostic('Browser error: ' + error.stack); });
  var origin = 'http://127.0.0.1:' + info.port;
  await page.goto(origin + '/fixture/login?to=alpha');
  await page.waitForFunction(function () { return !document.querySelector('.icon-strip-brand').disabled; });
  var projectSession = await page.evaluate(async function () { return (await import('/p/alpha/modules/store.js')).store.get('activeSessionId'); });
  await page.locator('#input').fill('Original project draft');
  var trigger = page.locator('.icon-strip-brand');
  await trigger.click();
  assert.equal(await page.locator('#contextual-clay [data-clay-expand] span').textContent(), 'Open Clay');
  var frame = page.frameLocator('#contextual-clay iframe');
  await frame.locator('#messages').getByText('Isolated canonical integration conversation.', { exact: true }).waitFor();
  await frame.locator('#input-wrapper:not([inert])').waitFor();
  var input = frame.locator('#input');
  await input.fill('Help me organise this project. Keep my current session open while we review the next steps.');
  var frameWindow = page.frames().find(function (item) { return item.url().includes('contextual=1'); });
  await frameWindow.evaluate(async function () {
    window.rendererIdentity = 'preserved'; window.sent = [];
    var ws = (await import('./modules/ws-ref.js')).getWs(); var send = ws.send.bind(ws);
    ws.send = function (value) { window.sent.push(JSON.parse(value)); return send(value); };
  });
  for (var width of [1280, 390]) {
    await page.setViewportSize({ width: width, height: 844 });
    await frame.locator('#contextual-attach-btn').click();
    for (var selector of ['#attach-file-btn', '#attach-image-btn']) {
      assert.equal(await frame.locator(selector).isVisible(), true, selector + ' is available through + at ' + width);
      assert.ok((await frame.locator(selector).boundingBox()).width >= 36);
    }
    await frame.locator('#attach-file-btn').press('Escape');
  }
  await frame.locator('#contextual-attach-btn').click();
  var fileChooser = page.waitForEvent('filechooser');
  await frame.locator('#attach-file-btn').click();
  await (await fileChooser).setFiles({ name: 'handoff.txt', mimeType: 'text/plain', buffer: Buffer.from('Isolated attachment continuity check.') });
  await frame.locator('#image-preview-bar').getByText('handoff.txt', { exact: false }).waitFor();
  await frame.locator('#contextual-attach-btn').click();
  var imageChooser = page.waitForEvent('filechooser');
  await frame.locator('#attach-image-btn').click();
  await (await imageChooser).setFiles({ name: 'pixel.png', mimeType: 'image/png', buffer: Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aN1sAAAAASUVORK5CYII=', 'base64') });
  await frame.locator('#image-preview-bar img').waitFor();
  var selectedViewer = await browser.newPage();
  await selectedViewer.goto(origin + '/fixture/login?to=alpha');
  await selectedViewer.goto(origin + '/p/beta/');
  await selectedViewer.locator('#messages').getByText('BETA_RENDERED_DESTINATION', { exact: true }).waitFor();
  await page.bringToFront();
  var review = page.request.get(origin + '/fixture/propose?viewer=beta&target=alpha');
  await frame.locator('.ask-user-container:not(.answered)').waitFor();
  var reviewId = await frame.locator('.ask-user-container:not(.answered)').getAttribute('data-tool-id');
  await page.getByRole('button', { name: 'Open Clay', exact: true }).click();
  await page.waitForURL('**/p/' + info.sourceSlug + '/');
  await page.waitForFunction(function (session) { return document.body.classList.contains('contextual-clay-native') && document.getElementById('input').value.includes('Help me organise') && !document.getElementById('input-wrapper').inert; }, info.sourceSessionId);
  assert.equal(await page.evaluate(async function () { var state = (await import('/p/alpha/modules/store.js')).store; return state.get('currentSlug'); }), info.sourceSlug);
  assert.equal(await page.evaluate(async function () { return (await import('/p/alpha/modules/store.js')).store.get('activeSessionId'); }), info.sourceSessionId);
  assert.equal(await page.locator('body').evaluate(function (body) { return body.classList.contains('mate-workspace-active'); }), true);
  assert.equal(await page.locator('#contextual-clay').isVisible(), false, 'No expanded iframe overlay');
  assert.equal(await input.inputValue(), '', 'The hidden popover does not keep a second draft');
  assert.equal(await input.evaluate(function (input) { return input.readOnly; }), true);
  assert.match(await page.locator('#image-preview-bar').textContent(), /handoff.txt/);
  assert.equal(await page.locator('#image-preview-bar img').count(), 1);
  await page.evaluate(async function () {
    window.nativeSent = []; var ws = (await import('/p/alpha/modules/ws-ref.js')).getWs(); var send = ws.send.bind(ws);
    ws.send = function (value) { window.nativeSent.push(JSON.parse(value)); return send(value); };
  });
  var nativeReview = page.locator('.ask-user-container[data-tool-id="' + reviewId + '"]');
  await nativeReview.getByText('Keep unchanged', { exact: true }).click();
  await nativeReview.locator('.ask-user-submit').click();
  assert.equal((await (await review).json()).status, 'cancelled');
  assert.equal(await page.evaluate(function () { return window.nativeSent.filter(function (event) { return event.type === 'ask_user_response'; }).length; }), 1);
  assert.equal(await frameWindow.evaluate(function () { return window.sent.filter(function (event) { return event.type === 'ask_user_response'; }).length; }), 0);
  await page.evaluate(async function () { var store = (await import('/p/alpha/modules/store.js')).store; window.nativeStore = store; window.nativeWsModule = await import('/p/alpha/modules/ws-ref.js'); window.handoffs = []; store.subscribe(function (value, prev) { if (value.contextualWorkspace !== prev.contextualWorkspace) window.handoffs.push({ stage: value.contextualWorkspace && value.contextualWorkspace.stage, images: value.contextualWorkspace && value.contextualWorkspace.draft && value.contextualWorkspace.draft.images.length }); }); });
  await page.bringToFront();
  await page.evaluate(async function () { window.oldNativeSocket = (await import('/p/alpha/modules/ws-ref.js')).getWs(); window.oldNativeSocket.close(); });
  await page.waitForFunction(function () { return window.oldNativeSocket.readyState === 3 && window.handoffs.some(function (handoff) { return handoff.stage === 'opening'; }); });
  await page.waitForFunction(function () { return window.nativeStore.get('connected') && window.nativeStore.get('contextualWorkspace').stage === 'workspace' && !window.nativeStore.get('replayingHistory') && window.nativeWsModule.getWs() !== window.oldNativeSocket && document.getElementById('input').value.includes('Help me organise') && document.querySelectorAll('#image-preview-bar img').length === 1 && !document.getElementById('input-wrapper').inert; });
  t.diagnostic('Reconnect state: ' + JSON.stringify(await page.evaluate(async function () { var store = (await import('/p/alpha/modules/store.js')).store; return { stage: store.get('contextualWorkspace').stage, connected: store.get('connected'), hidden: document.hidden, images: document.querySelectorAll('#image-preview-bar img').length, native: document.body.className, input: document.getElementById('input').value }; })));
  t.diagnostic('Reconnect handoffs: ' + JSON.stringify(await page.evaluate(function () { return window.handoffs; })));
  assert.equal(await page.locator('#image-preview-bar img').count(), 1, 'Reconnect preserves attachment ownership');
  assert.match(await page.locator('#image-preview-bar').textContent(), /handoff.txt/);
  await page.evaluate(async function () {
    window.nativeStop = []; var ws = (await import('/p/alpha/modules/ws-ref.js')).getWs(); var send = ws.send.bind(ws);
    ws.send = function (value) { window.nativeStop.push(JSON.parse(value)); return send(value); };
  });
  var nativeDraft = 'Edited in the native Clay workspace. Help me organise this project. Keep my current session open while we review the next steps.';
  await page.locator('#input').fill(nativeDraft);
  await page.request.get(origin + '/fixture/stream');
  await page.locator('#messages').getByText('CONTEXTUAL_STREAM_CONTINUES', { exact: false }).waitFor();
  for (var width of [1280, 390]) {
    await page.setViewportSize({ width: width, height: 844 });
    await page.waitForFunction(function () { var input = document.getElementById('input'); return input.scrollHeight <= input.clientHeight + 1 || getComputedStyle(input).overflowY === 'auto'; });
    var stop = await page.locator('#generation-stop').boundingBox();
    var mic = await page.locator('#stt-btn').boundingBox();
    assert.ok(stop.x + stop.width <= mic.x, 'Native Stop has a separate slot at ' + width);
    assert.equal(await page.locator('#input').inputValue(), nativeDraft);
    await page.screenshot({ path: '/tmp/contextual-native-mate-' + width + '.png', animations: 'disabled' });
    await page.evaluate(async function () { (await import('/p/alpha/modules/theme.js')).applyTheme('clay-dark'); });
    await page.screenshot({ path: '/tmp/contextual-native-mate-dark-' + width + '.png', animations: 'disabled' });
    await page.evaluate(async function () { (await import('/p/alpha/modules/theme.js')).applyTheme('clay-light'); });
  }
  await page.locator('#generation-stop').click();
  assert.equal(await page.evaluate(function () { return window.nativeStop.filter(function (event) { return event.type === 'stop'; }).length; }), 1);
  assert.equal(await frameWindow.evaluate(function () { return window.sent.filter(function (event) { return event.type === 'stop'; }).length; }), 0);
  await page.getByRole('button', { name: 'Back to project', exact: true }).click();
  await page.waitForURL('**/p/alpha/');
  await page.waitForFunction(function () { return document.getElementById('input').value === 'Original project draft' && !document.getElementById('input-wrapper').inert; });
  assert.equal(await page.evaluate(async function () { return (await import('/p/alpha/modules/store.js')).store.get('activeSessionId'); }), projectSession);
  await trigger.click();
  assert.equal(await input.inputValue(), nativeDraft);
  assert.equal(await frameWindow.evaluate(function () { return window.rendererIdentity; }), 'preserved');
  assert.equal(await input.evaluate(function (input) { return input.readOnly; }), false);
  assert.equal(await frame.locator('#image-preview-bar img').count(), 1);
  assert.match(await frame.locator('#image-preview-bar').textContent(), /handoff.txt/);
  await frame.locator('#messages').getByText('CONTEXTUAL_STREAM_CONTINUES', { exact: false }).waitFor();
  assert.equal(await frame.locator('.ask-user-container[data-tool-id="' + reviewId + '"]').getAttribute('class').then(function (value) { return value.includes('answered'); }), true);
  await trigger.click();
  await page.waitForURL('**/p/' + info.sourceSlug + '/');
  await page.locator('#contextual-workspace-back').waitFor();
  await trigger.click();
  await page.waitForURL('**/p/alpha/');
  await trigger.click();
  await page.request.get(origin + '/fixture/remove-contextual-session');
  await frameWindow.evaluate(async function () { (await import('./modules/ws-ref.js')).getWs().close(); });
  await frame.locator('#contextual-session-warning').waitFor();
  await page.getByRole('button', { name: 'Open Clay', exact: true }).click();
  assert.equal(page.url(), origin + '/p/alpha/', 'A removed exact conversation cannot silently open a different native session');
  assert.equal(await input.inputValue(), nativeDraft);
  t.diagnostic('Actual route/native Mate shell, exact session, review decline, stream, draft/image/file transfer, direct file chooser handlers at both widths, Back and trigger cycle passed.');
});