In organization mode #/ is the cross-organization dashboard (sample 02), #/projects lists every organization's projects with sort, filters and the shared By org / Flat grouping (sample 04), and #/inbox lists decisions across organizations with per-organization decide paths and terminal commands carrying --org and the full id (sample 05). The per-organization board moves to #/board and the organization's own project list to #/chat, which keeps the project surface. Reads go through workspace-data.js; a read an organization could not make says not read, never zero. Polling repaints in place and keeps focus, caret and scroll.
45 lines
3.1 KiB
JavaScript
45 lines
3.1 KiB
JavaScript
// org-lan-bind: a browser on another machine opens the dev owner Console by an allowed LAN name
|
|
// (http://lan.test:PORT/, mapped to 127.0.0.1 for the test) and creates a project. That origin
|
|
// is not a secure context, so Chromium sends no Fetch Metadata and has no crypto.randomUUID.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { startServer } from '../src/serve.mjs';
|
|
import { createDevOwner } from '../src/dev-owner.mjs';
|
|
import { browser } from './browser.mjs';
|
|
import { close } from './fixture.mjs';
|
|
|
|
const ID = '20000000-0000-4000-8000-000000000002';
|
|
|
|
test('an allowed LAN name loads the org page and creates a project', { timeout: 45000 }, async t => {
|
|
const projects = [], created = [];
|
|
const organizations = {
|
|
list: async () => [{ id: 'alpha', label: 'alpha', busHealthy: true }],
|
|
resolve: async id => id === 'alpha' ? {
|
|
busHealthy: true, send: async () => ({}),
|
|
reads: { business: async () => ({ business: { id, projects: ['work'] } }), busStatus: async () => ({ live: true }) },
|
|
bus: { inbox: async () => [], agents: async () => [], messages: async () => ({ messages: [] }), tasks: async () => [] },
|
|
chat: {
|
|
projects: async () => projects, threads: async () => [],
|
|
createProject: async args => { created.push(args); const row = { id: ID, ...args }; projects.push(row); return row; },
|
|
},
|
|
} : null,
|
|
};
|
|
const server = await startServer({ host: '0.0.0.0', port: 0, devAllowHosts: ['lan.test'], sso: createDevOwner(), organizations, send: async () => ({}), board: 'http://127.0.0.1:9', log: () => {} });
|
|
t.after(() => close(server));
|
|
const port = server.address().port;
|
|
const b = await browser({ machine: false, args: ['--host-resolver-rules=MAP lan.test 127.0.0.1'] }); t.after(() => b.close());
|
|
const wait = expression => b.evaluate(`(async()=>{for(let i=0;i<300;i++){if(${expression})return;await new Promise(r=>setTimeout(r,30))}throw Error(${JSON.stringify(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()`);
|
|
await b.navigate(`http://lan.test:${port}/#/orgs/alpha/chat`);
|
|
assert.deepEqual(await b.evaluate('({ host: location.host, secure: isSecureContext, uuid: typeof crypto.randomUUID })'), { host: `lan.test:${port}`, secure: false, uuid: 'undefined' });
|
|
await wait('window.MosaicOrg?.id === "alpha" && !!document.querySelector("#chat-repo option[value=work]")');
|
|
await click('.project-create summary');
|
|
await input('#chat-project-name', 'Work'); await input('#chat-repo', 'work');
|
|
await click('#chat-project-form button[type=submit]');
|
|
await wait('/\\/projects\\/[0-9a-f-]{36}/i.test(location.hash)');
|
|
assert.equal(await b.evaluate('location.hash'), `#/orgs/alpha/projects/${ID}`);
|
|
assert.deepEqual(created, [{ name: 'Work', repo: 'work' }]);
|
|
assert.equal(await b.evaluate('location.host'), `lan.test:${port}`);
|
|
});
|