Files
stack/packages/webui/tests/org-release-browser.mjs

91 lines
6.4 KiB
JavaScript

// Run against a fresh scratch Console with the fixture model already started.
// All organization setup and worker actions below use the actual browser UI.
import assert from 'node:assert/strict';
import { mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { browser } from './browser.mjs';
const base = process.env.ORG_BROWSER_URL, shots = process.env.ORG_BROWSER_SHOTS;
if (!base || !shots) throw Error('ORG_BROWSER_URL and ORG_BROWSER_SHOTS are required');
await mkdir(shots, { recursive: true });
const b = await browser({ machine: false });
const wait = async (expression, timeout = 20000) => {
const end = Date.now() + timeout;
while (Date.now() < end) {
try { if (await b.evaluate(`Boolean(${expression})`)) return; } catch { /* a navigation replaces the page */ }
await new Promise(r => setTimeout(r, 50));
}
throw Error(`Timed out waiting for ${expression}`);
};
const input = (selector, value) => b.evaluate(`(()=>{const e=document.querySelector(${JSON.stringify(selector)});e.value=${JSON.stringify(value)};e.dispatchEvent(new Event('input',{bubbles:true}));e.dispatchEvent(new Event('change',{bubbles:true}));})()`);
const click = selector => b.evaluate(`document.querySelector(${JSON.stringify(selector)}).click()`);
try {
await b.viewport(1440, 900);
await b.navigate(`${base.replace(/\/$/, '')}/#/`);
await wait('document.querySelector("#held")?.textContent.includes("No organizations yet")');
await b.screenshot(join(shots, '00-empty.png'));
await click('#held a[href="#/settings/organizations"]');
await wait('document.querySelector("#org-new-form [data-write]") && !document.querySelector("#org-new-form [data-write]").disabled');
await input('#org-new-id', 'alpha'); await click('#org-new-form [data-write]');
await wait('document.querySelector("#org-editor") && !document.querySelector("#org-editor").hidden');
await b.screenshot(join(shots, '01-create.png'));
await click('#org-save');
await wait('document.querySelector("[data-step=queue] [data-risk]")');
await b.screenshot(join(shots, '02-setup.png'));
for (const step of ['queue', 'project:work']) {
const selector = `[data-step="${step}"]`;
await click(`${selector} [data-risk]`); await click(`${selector} [data-run]`);
await wait(`document.querySelector(${JSON.stringify(selector + ' .org-pill')})?.textContent === 'Done'`);
}
await click('[data-step="bus"] [data-run]');
await wait('document.querySelector("[data-step=bus] .org-pill")?.textContent === "Done"', 60000);
await b.navigate(`${base.replace(/\/$/, '')}/?org-browser=1#/orgs/alpha/settings/organizations?org=alpha&step=setup`);
await wait('document.querySelector("#org-setup") && !document.querySelector("#org-setup").hidden');
assert.match(await b.evaluate('location.hash'), /^#\/orgs\/alpha\/settings\/organizations\?org=alpha&step=setup$/);
await click('#org-save');
await wait('document.querySelector("#org-receipt")?.textContent.includes("Changes saved")');
assert.match(await b.evaluate('location.hash'), /^#\/orgs\/alpha\/settings\/organizations\?org=alpha/);
await b.navigate(`${base.replace(/\/$/, '')}/?org-browser=2#/orgs/alpha/settings/organizations?org=alpha&step=setup`);
await wait('document.querySelector("#org-setup") && !document.querySelector("#org-setup").hidden');
assert.match(await b.evaluate('location.hash'), /^#\/orgs\/alpha\/settings\/organizations\?org=alpha&step=setup$/);
await b.screenshot(join(shots, '03-resume.png'));
await b.evaluate(`location.hash='#/orgs/alpha/projects'`);
await wait('!!document.querySelector("#chat-repo option[value=work]")');
await click('.project-create summary');
await input('#chat-project-name', 'Work'); await input('#chat-repo', 'work');
await b.screenshot(join(shots, '03-linked-project.png'));
await click('#chat-project-form button[type=submit]');
await wait('location.hash.match(/\\/projects\\/[0-9a-f-]{36}/i)');
const projectRoute = await b.evaluate('location.hash');
await b.evaluate(`location.hash=${JSON.stringify(projectRoute + '/conversations')}`);
await wait('!!document.querySelector("#chat-thread-form")');
await click('.project-new-conversation summary');
await input('#chat-thread-name', 'Trial'); await input('#chat-instance', 'assistant');
await click('#chat-thread-form button[type=submit]');
await wait('!!document.querySelector("#chat-body")', 30000);
await input('#chat-body', 'Reply with fixture word'); await click('#chat-send-form button[type=submit]');
await wait('document.querySelector(".chat-log")?.textContent.includes("PINEAPPLE")', 90000);
assert.match(await b.evaluate('document.querySelector(".chat-log").textContent'), /PINEAPPLE/);
await b.screenshot(join(shots, '04-fixture-reply.png'));
// Start the second creation in a fresh page so the chat form state does not
// count as an unrelated draft during the organization switch.
await b.navigate(`${base.replace(/\/$/, '')}/#/orgs/alpha/settings/organizations`);
await wait('document.querySelector("#org-new-form [data-write]") && !document.querySelector("#org-new-form [data-write]").disabled');
await input('#org-new-id', 'beta'); await click('#org-new-form [data-write]');
await wait('document.querySelector("#org-editor") && !document.querySelector("#org-editor").hidden');
await b.evaluate(`(()=>{const e=document.querySelector('#org-json-text'), d=JSON.parse(e.value);d.appearance={theme:'cobalt',mode:'light'};e.value=JSON.stringify(d,null,2);e.dispatchEvent(new Event('input',{bubbles:true}));})()`);
await click('#org-save');
await wait('window.MosaicOrg?.id === "beta" && document.querySelector("#org-setup") && !document.querySelector("#org-setup").hidden');
await b.evaluate('window.MosaicAppearance.ready');
assert.match(await b.evaluate('location.hash'), /^#\/orgs\/beta\/settings\/organizations\?org=beta&step=setup/);
assert.match(await b.evaluate('document.querySelector("header").textContent'), /beta/);
assert.equal(await b.evaluate('document.documentElement.dataset.palette'), 'cobalt');
assert.match(await b.evaluate('document.querySelector("#org-receipt").textContent'), /Organization created.*beta/s);
await b.screenshot(join(shots, '05-created-beta.png'));
console.log('Browser organization setup and linked project fixture reply passed');
} catch (error) {
await b.screenshot(join(shots, 'failure.png')).catch(() => {});
console.error(await b.evaluate('document.body.innerText.slice(0, 5000)').catch(() => 'page unavailable'));
throw error;
} finally { await b.close(); }