Ctrl K
ComunidadGuías…

test/contextual-window-lifecycle-browser.test.cjs

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

Archivo original ↗Preguntar a KiBot
var test = require('node:test'), assert = require('node:assert/strict'), cp = require('node:child_process'), path = require('node:path'), playwright = require('playwright');

test('Clay and Mate windows retain conversations through navigation and refresh the exact current context', async function (t) {
  var child = cp.fork(path.join(__dirname, 'fixtures/contextual-clay-server.cjs'), [], { env: Object.assign({}, process.env, { CLAY_CONTEXT_ACCESS_FIXTURE: '1' }), stdio: ['ignore', 'pipe', 'pipe', 'ipc'] });
  t.after(function () { child.kill('SIGTERM'); });
  var output = ''; child.stdout.on('data', function (data) { output += data; }); child.stderr.on('data', function (data) { output += data; });
  var info = await new Promise(function (resolve, reject) { var timer = setTimeout(function () { reject(new Error(output)); }, 15000); child.once('message', function (value) { clearTimeout(timer); resolve(value); }); });
  var browser = await playwright.chromium.launch(); t.after(function () { return browser.close(); });
  var page = await browser.newPage({ viewport: { width: 1280, height: 800 }, reducedMotion: 'reduce' }); page.setDefaultTimeout(10000);
  var origin = 'http://127.0.0.1:' + info.port, errors = [];
  page.on('pageerror', function (error) { errors.push(error.message); });
  await page.goto(origin + '/fixture/login?to=alpha', { waitUntil: 'domcontentloaded' });
  await page.waitForFunction(function () { return !document.querySelector('.icon-strip-brand').disabled; });
  await page.evaluate(async function () { window.lifecycleStore = (await import('./modules/store.js')).store; });
  var destination = await (await page.request.get(origin + '/fixture/state')).json();
  var targets = [{ id: info.sourceSlug.slice(5), slug: info.sourceSlug, name: 'Clay' }, info.ordinaryMates[0]];
  function surface() { return page.locator('#contextual-clay'); }
  function frame() { return page.frameLocator('#contextual-clay iframe:visible'); }
  async function ready() { await page.waitForFunction(function () { var pane = document.querySelector('#contextual-clay iframe:not([hidden])'); return pane && pane.conversation && pane.conversation.identity().ready; }); }
  async function current() { return page.evaluate(function () { var prepared = window.lifecycleStore.get('contextualPrepared'); return { requestId: prepared.requestId, ref: prepared.contextRef, status: prepared.status, snapshot: JSON.parse(document.getElementById('contextual-clay').dataset.screenReference) }; }); }
  async function select(slug, session) { await page.evaluate(async function (target) { (await import('/p/alpha/modules/project-session-navigation.js')).openProjectSession(target.slug, target.session); }, { slug: slug, session: session }); await page.waitForFunction(function (target) { return window.lifecycleStore.get('currentSlug') === target.slug && window.lifecycleStore.get('activeSessionId') === target.session && !window.lifecycleStore.get('replayingHistory') && window.lifecycleStore.get('sessionActivatedProjectSlug') === target.slug; }, { slug: slug, session: session }); }
  async function restricted(name, args) { return (await page.request.get(origin + '/fixture/context-tool?name=' + name + '&args=' + encodeURIComponent(JSON.stringify(args || {})))).json(); }
  for (var target of targets) {
    await select('alpha', destination.alphaSessionId);
    var trigger = page.locator(target.name === 'Clay' ? '.icon-strip-brand' : '.icon-strip-project-mate[data-mate-id="' + target.id + '"]');
    await trigger.click(); await ready();
    var paneWindow = page.frames().find(function (item) { return item.url().includes(target.slug); });
    await paneWindow.evaluate(function () { window.lifecycleRenderer = 'retained'; });
    if (target.name !== 'Clay') {
      await page.locator('[data-context-status]').filter({ hasText: 'Next message' }).waitFor();
      await frame().locator('#input').fill('Share this exact Alpha conversation.'); await frame().locator('#send-btn').click();
      await page.locator('[data-context-status]').filter({ hasText: 'Shared' }).waitFor();
      var shared = await restricted('read_current_context'); assert.equal(shared.status, 'shared_snapshot');
    }
    var draft = target.name + ' retained draft'; await frame().locator('#input').fill(draft);
    var first = await current();
    await page.mouse.click(1100, 650);
    assert.equal(await surface().isVisible(), true, 'Outside click keeps ' + target.name + ' open');
    await page.locator('#input').focus(); await page.keyboard.press('Escape');
    assert.equal(await surface().isVisible(), true, 'Escape in the background belongs to the background');
    await page.getByRole('link', { name: 'BI', exact: true }).click(); await page.waitForURL('**/p/beta/');
    await page.locator('#messages').getByText('BETA_RENDERED_DESTINATION', { exact: true }).waitFor();
    await page.locator('[data-context-project]').filter({ hasText: 'Beta' }).waitFor();
    assert.equal(await surface().isVisible(), true);
    assert.equal(await frame().locator('#input').inputValue(), draft);
    assert.equal(await paneWindow.evaluate(function () { return window.lifecycleRenderer; }), 'retained');
    var beta = await current(); assert.notEqual(beta.requestId, first.requestId); assert.equal(beta.snapshot.projectSlug, 'beta'); assert.equal(beta.snapshot.sessionId, destination.betaSessionId);
    if (target.name !== 'Clay') { assert.equal((await restricted('read_selected_context', { contextRef: shared.contextRef })).status, 'failed', 'Old query loses access after navigation'); }
    // Create and select through the same production session service used by the sidebar.
    await page.evaluate(async function () { (await import('/p/alpha/modules/sidebar-sessions.js')).startNewSession('claude'); });
    await page.waitForFunction(function (old) { return window.lifecycleStore.get('activeSessionId') !== old && !window.lifecycleStore.get('replayingHistory'); }, destination.betaSessionId);
    var next = await current(); assert.notEqual(next.requestId, beta.requestId); assert.notEqual(next.snapshot.sessionId, beta.snapshot.sessionId);
    assert.equal(await surface().isVisible(), true); assert.equal(await frame().locator('#input').inputValue(), draft);
    // Hold a real preparation reply, change to Home, then release the stale reply.
    await page.evaluate(async function () {
      var socket = (await import('/p/alpha/modules/ws-ref.js')).getWs(), receive = socket.onmessage;
      window.heldPreparation = null;
      socket.onmessage = function (event) { if (JSON.parse(event.data).type === 'mate_context_prepared' && !window.heldPreparation) { window.heldPreparation = event; return; } receive(event); };
      window.releasePreparation = function () { socket.onmessage = receive; receive(window.heldPreparation); };
    });
    await select('beta', destination.betaSessionId);
    await page.waitForFunction(function () { return !!window.heldPreparation; });
    // The standalone project list is gone; the Home context is the zero-project welcome surface, entered here through its store flag while the list is still loading.
    await page.evaluate(async function () { (await import('/p/alpha/modules/store.js')).store.set({ projectListLoaded: false, projectsHubVisible: true, pendingHomeProjectSlug: null }); });
    var home = await current(); assert.equal(home.snapshot.screen, 'Home'); assert.equal(home.snapshot.projectTitle, 'Projects'); assert.equal(home.snapshot.sessionId, null); assert.equal(home.snapshot.projectSlug, null); assert.equal(home.snapshot.conversationTitle, null);
    await page.evaluate(function () { window.releasePreparation(); });
    assert.equal((await current()).requestId, home.requestId, 'Stale preparation cannot replace Home context');
    if (target.name === 'Clay') {
      var sentSnapshot = await paneWindow.evaluate(async function () { return (await import('./modules/contextual-clay-session.js')).contextualScreenReference(); });
      assert.equal(sentSnapshot.screen, 'Home');
      assert.equal(sentSnapshot.projectTitle, 'Projects');
    }
    assert.equal(!!(await current()).ref, false);
    assert.notEqual(await page.evaluate(function () { return JSON.parse(window.heldPreparation.data).requestId; }), home.requestId);
    assert.equal(await surface().isVisible(), true); assert.equal(await frame().locator('#input').inputValue(), draft);
    await page.evaluate(async function () { (await import('/p/alpha/modules/store.js')).store.set({ projectsHubVisible: false, projectListLoaded: true }); });
    await select('alpha', destination.alphaSessionId);
    await page.locator('[data-context-project]').filter({ hasText: 'Alpha' }).waitFor();
    for (var width of [1280, 390]) {
      await page.setViewportSize({ width: width, height: 844 });
      var label = page.getByRole('button', { name: 'New chat', exact: true });
      assert.equal((await label.innerText()).trim(), 'New chat'); assert.equal(await label.locator('span').isVisible(), true);
      var labelBox = await label.boundingBox(), shellBox = await surface().boundingBox();
      assert.ok(labelBox.x >= shellBox.x && labelBox.x + labelBox.width <= shellBox.x + shellBox.width);
      assert.equal(await surface().evaluate(function (el) { return el.scrollWidth <= el.clientWidth; }), true);
      await page.screenshot({ path: '/tmp/contextual-window-' + target.name + '-' + width + '.png', animations: 'disabled' });
    }
    await page.setViewportSize({ width: 1280, height: 800 });
    await frame().locator('#input').press('Escape'); assert.equal(await surface().isVisible(), false);
    await trigger.click(); await ready(); assert.equal(await frame().locator('#input').inputValue(), draft);
    await page.locator('[data-clay-close]').click(); assert.equal(await surface().isVisible(), false);
    await trigger.click(); await ready(); await trigger.click(); await page.waitForURL('**/p/' + target.slug + '/');
    await page.waitForFunction(function () { return document.body.classList.contains('contextual-clay-native') && !document.getElementById('input-wrapper').inert; });
    assert.equal(await surface().isVisible(), false); assert.equal(await page.locator('#input').inputValue(), draft);
    await page.locator('#contextual-workspace-back').click(); await page.waitForURL('**/p/alpha/');
    await page.waitForFunction(function () { return !window.lifecycleStore.get('contextualWorkspace'); });
    await trigger.click(); await ready();
    await page.evaluate(function (id) { window.lifecycleMates = window.lifecycleStore.get('cachedMatesList'); window.lifecycleStore.set({ cachedMatesList: window.lifecycleMates.filter(function (mate) { return mate.id !== id; }) }); }, target.id);
    assert.equal(await surface().isVisible(), false); assert.equal(await page.locator('#contextual-clay iframe[data-mate-id="' + target.id + '"]').count(), 0, 'Deleted identity tears down the renderer');
    await page.evaluate(function () { window.lifecycleStore.set({ cachedMatesList: window.lifecycleMates }); });
  }
  // The canonical brand returns from its own native route; ordinary Mate icons can lend a direct native composer.
  for (var nativeTarget of targets.filter(function (item) { return item.name !== 'Clay'; })) {
    var direct = await browser.newPage({ viewport: { width: 1280, height: 800 }, reducedMotion: 'reduce' }); direct.setDefaultTimeout(10000);
    await direct.goto(origin + '/fixture/login?to=alpha', { waitUntil: 'domcontentloaded' });
    await direct.goto(origin + '/p/' + nativeTarget.slug + '/', { waitUntil: 'domcontentloaded' });
    await direct.evaluate(async function () { window.directLifecycleStore = (await import('./modules/store.js')).store; });
    await direct.waitForFunction(function () { return window.directLifecycleStore.get('activeSessionId') && !window.directLifecycleStore.get('replayingHistory'); });
    var nativeDraft = 'Direct native draft for ' + nativeTarget.name;
    await direct.locator('#input').fill(nativeDraft);
    var nativeTrigger = direct.locator(nativeTarget.name === 'Clay' ? '.icon-strip-brand' : '.icon-strip-project-mate[data-mate-id="' + nativeTarget.id + '"]');
    await nativeTrigger.click();
    var directPane = direct.frameLocator('#contextual-clay iframe:visible');
    await directPane.locator('#input-wrapper:not([inert])').waitFor();
    await direct.waitForFunction(function (draft) { var pane = document.querySelector('#contextual-clay iframe:not([hidden])'); return pane && pane.contentDocument.getElementById('input').value === draft; }, nativeDraft);
    await direct.getByRole('link', { name: 'BI', exact: true }).click(); await direct.waitForURL('**/p/beta/');
    await direct.locator('#messages').getByText('BETA_RENDERED_DESTINATION', { exact: true }).waitFor();
    assert.equal(await direct.locator('#contextual-clay').isVisible(), true);
    assert.equal(await directPane.locator('#input').inputValue(), nativeDraft, 'The pane retains its draft after leaving its direct native origin');
    assert.equal(await directPane.locator('#input').getAttribute('readonly'), null);
    assert.notEqual(await direct.locator('#input').inputValue(), nativeDraft, 'The destination composer never receives another Mate draft');
    assert.equal(await direct.evaluate(function () { return window.directLifecycleStore.get('contextualNativeOrigin'); }), null);
    await direct.locator('[data-clay-expand]').click(); await direct.waitForURL('**/p/' + nativeTarget.slug + '/');
    await direct.waitForFunction(function () { return document.body.classList.contains('contextual-clay-native') && !document.getElementById('input-wrapper').inert; });
    assert.equal(await direct.locator('#input').inputValue(), nativeDraft);
    var other = targets.find(function (item) { return item.id !== nativeTarget.id; });
    await direct.locator(other.name === 'Clay' ? '.icon-strip-brand' : '.icon-strip-project-mate[data-mate-id="' + other.id + '"]').click();
    await directPane.locator('#input-wrapper:not([inert])').waitFor();
    await directPane.locator('#input').fill('Other floating writer');
    await direct.getByRole('link', { name: 'BI', exact: true }).click(); await direct.waitForURL('**/p/beta/');
    await direct.locator('#messages').getByText('BETA_RENDERED_DESTINATION', { exact: true }).waitFor();
    assert.equal(await direct.locator('#contextual-clay').isVisible(), true, 'Leaving a full-screen Mate preserves another open floating conversation');
    assert.equal(await directPane.locator('#input').inputValue(), 'Other floating writer');
    await direct.evaluate(function () { window.directLifecycleStore.set({ myUserId: 'foreign' }); });
    assert.equal(await direct.locator('#contextual-clay').isVisible(), false);
    assert.equal(await direct.locator('#contextual-clay iframe').count(), 0, 'Owner change tears down every private pane');
    await direct.close();
  }
  assert.deepEqual(errors, []);
});