#/dashboard is the system landing route: one row per organization with its bus state, agent count (organization files), project count and needs-you count, each only when a read gave it, and active agents. Opening an organization from the sidebar or a dashboard row is a route inside the same page: no reload, a Dashboard / <organization> breadcrumb, and later /api reads scoped to it through the existing fetch wrapper, whose organization is now a variable the router sets (mosaic-org-switch event). #/orgs/<id>/... stays the address. An organization whose bus is down says so on the dashboard and on its page, with the reason the workspace read gives.
264 lines
23 KiB
JavaScript
264 lines
23 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { startServer } from '../src/serve.mjs';
|
|
import { createDevOwner } from '../src/dev-owner.mjs';
|
|
import { createOrgAdmin } from '../src/org-admin.mjs';
|
|
import { organizationConsole } from '../src/organizations.mjs';
|
|
import * as service from '../../organization/src/org-service.mjs';
|
|
import * as registry from '../../organization/src/org-registry.mjs';
|
|
import { orgPaths } from '../../cli/src/config.mjs';
|
|
import { busStopForDelete } from '../../cli/src/org-bus.mjs';
|
|
import { CliError } from '../../cli/src/errors.mjs';
|
|
import { actionTrail } from '../src/action-trail.mjs';
|
|
import { browser } from './browser.mjs';
|
|
import { close } from './fixture.mjs';
|
|
|
|
// E1: `bus` adds verbs to the admin's bus (busStart for a restore); `trail` gives the Console its action trail;
|
|
// `extra` adds to each organization's transports (the dashboard's bus reads and System types).
|
|
async function fixture(t, ids = ['alpha', 'beta'], { health = null, bus = {}, trail = false, extra = () => ({}) } = {}) {
|
|
const dir = await mkdtemp(join(tmpdir(), 'archive-browser-'));
|
|
const configDir = join(dir, 'config'), dataRoot = join(dir, 'data'); await mkdir(configDir, { mode: 0o700 }); await mkdir(dataRoot, { mode: 0o700 });
|
|
const ctx = { configDir, dataRoot, instance: 'test', principal: { provider: 'local-dev', iss: 'local-dev', sub: 'local-dev-owner' } };
|
|
for (const id of ids) await service.createOrg(ctx, id, await service.templateRender(id));
|
|
const busStatus = async () => ({ healthy: health ? health.up : false });
|
|
const organizations = organizationConsole({ ctx, listOrgs: registry.listOrgs, resolveOrg: registry.resolveOrg, showOrg: service.showOrg, orgPaths, busStatus, options: ({ id }) => ({ reads: { business: async () => ({ business: { id, projects: ['work'] } }), settings: async () => ({}) }, bus: {}, send: async () => ({}), ...extra(id) }) });
|
|
const admin = createOrgAdmin({ service, registry, bus: { busStatus, busStopForDelete, ...bus }, context: async () => ({ configDir, dataRoot, instance: 'test' }) });
|
|
const server = await startServer({ port: 0, sso: createDevOwner(), orgAdmin: admin, organizations, send: async () => ({}), board: 'http://127.0.0.1:9', log: () => {}, ...(trail ? { trail: actionTrail(join(dir, 'console')) } : {}) });
|
|
const b = await browser({ machine: false }); t.after(async () => { await b.close(); await close(server); await rm(dir, { recursive: true, force: true }); });
|
|
const wait = async expression => { for (let i = 0; i < 400; i++) { try { if (await b.evaluate(expression)) return; } catch {} await new Promise(r => setTimeout(r, 30)); } throw Error(expression); };
|
|
await b.navigate(`http://127.0.0.1:${server.address().port}/#/orgs/alpha/settings/organizations`);
|
|
await wait(`document.querySelector('#org-open-id')?.value === 'alpha'`);
|
|
async function open(id) { await b.evaluate(`document.querySelector('#org-open-id').value=${JSON.stringify(id)};document.querySelector('#org-open-form button').click()`); await wait(`document.querySelector('#org-editor-title')?.textContent === ${JSON.stringify(`Organization: ${id}`)}`); }
|
|
async function shots(state, selector) {
|
|
if (!process.env.ORG_ARCHIVE_SHOTS) return;
|
|
await mkdir(process.env.ORG_ARCHIVE_SHOTS, { recursive: true });
|
|
for (const [width, height] of [[1400, 900], [390, 844]]) for (const mode of ['light', 'dark']) {
|
|
await b.viewport(width, height); await b.evaluate(`document.documentElement.dataset.mode=${JSON.stringify(mode)};document.querySelector(${JSON.stringify(selector)}).scrollIntoView({block:'start'})`);
|
|
await b.screenshot(join(process.env.ORG_ARCHIVE_SHOTS, `${state}-${width}-${mode}.png`));
|
|
}
|
|
}
|
|
return { b, wait, open, shots, ctx, port: server.address().port, dir };
|
|
}
|
|
|
|
test('archive and restore a non-current organization', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click()`);
|
|
assert.equal(await f.b.evaluate(`document.activeElement.id`), 'org-archive-confirm');
|
|
await f.shots('archive', '#org-archive');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-cancel').click()`);
|
|
assert.equal(await f.b.evaluate(`document.activeElement.id`), 'org-archive-open');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'archived');
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-open-id').selectedOptions[0].textContent`), /archived/);
|
|
assert.deepEqual(await f.b.evaluate(`window.MosaicOrg.list.then(rows=>rows.map(row=>row.id))`), ['alpha']);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-save').disabled`), true);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-json-text').disabled`), true);
|
|
await f.shots('archived', '#org-archived');
|
|
const chat = await f.b.evaluate(`fetch('/api/orgs/beta/chat/projects').then(async r=>({status:r.status,body:await r.json()}))`);
|
|
assert.equal(chat.status, 409); assert.equal(chat.body.error, 'org-archived');
|
|
await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`!document.querySelector('#org-archive-success').hidden`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'active');
|
|
assert.deepEqual(await f.b.evaluate(`window.MosaicOrg.list.then(rows=>rows.map(row=>row.id))`), ['alpha', 'beta']);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-save').disabled`), false);
|
|
await f.shots('restored', '#org-archive-success');
|
|
});
|
|
|
|
test('archive does not announce success for an unexpected response', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`const original=fetch.bind(window);window.fetch=(input,init)=>String(input).endsWith('/archive')?Promise.resolve(new Response(JSON.stringify({outcome:'done',step:'bus'}),{status:200})):original(input,init);document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!!document.querySelector('#org-archive-error').textContent`);
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-archive-error').textContent`), /outcome is unknown/i);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-archive-final').textContent`), 'Archive');
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-archived').hidden`), true);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'active');
|
|
});
|
|
|
|
test('archiving the current organization switches to the remaining one', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('alpha');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
// #1873 SP1: the breadcrumb, not the header, names the open organization.
|
|
await f.wait(`window.MosaicOrg?.id === 'beta' && /beta/i.test(document.querySelector('#breadcrumbs [data-crumb-org]')?.textContent ?? '')`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'alpha').state, 'archived');
|
|
});
|
|
|
|
test('opening another editor resets the archive confirmation and error', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-error').textContent='old error';document.querySelector('#org-archive-final').disabled=true`);
|
|
await f.open('alpha');
|
|
assert.deepEqual(await f.b.evaluate(`({trigger:document.querySelector('#org-archive-open').hidden,panel:document.querySelector('#org-archive-confirm').hidden,error:document.querySelector('#org-archive-error').textContent,disabled:document.querySelector('#org-archive-final').disabled})`), {trigger:false,panel:true,error:'',disabled:false});
|
|
});
|
|
|
|
// E1 (P1-4): one Restore restores the organization and starts its bus; the receipt names both.
|
|
const startingBus = (health, fails = 0) => {
|
|
const starts = [];
|
|
return { starts, busStart: async (ctx, id, opts) => { starts.push([id, opts]); if (starts.length <= fails) throw new CliError('bus failed to start; see the bus log', 3); health.up = true; return { org: id, pid: 4242, socket: join(ctx.dataRoot, 'bus.sock'), root: join(ctx.dataRoot, 'org-runtime', id) }; } };
|
|
};
|
|
const archiveBeta = async f => { await f.open('beta'); await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`); await f.wait(`!document.querySelector('#org-archived').hidden`); };
|
|
|
|
test('E1 (P1-4): Restore restores the organization and starts its bus, with one receipt naming both', { timeout: 45000 }, async t => {
|
|
const health = { up: false }, bus = startingBus(health);
|
|
const f = await fixture(t, ['alpha', 'beta'], { health, bus, trail: true }); await archiveBeta(f);
|
|
await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`!document.querySelector('#org-archive-success').hidden`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'active');
|
|
assert.deepEqual(bus.starts, [['beta', { detach: true, devLocalOwner: true, pm: false }]]);
|
|
const text = await f.b.evaluate(`document.querySelector('#org-archive-success').innerText`);
|
|
assert.match(text, /^Organization restored\. Bus started\. It answers\./);
|
|
assert.match(text, /Same as mosaic bus start beta --dev-local-owner --detach/);
|
|
assert.doesNotMatch(text, /remains stopped/);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('[data-restore-bus]')`), null, 'a running bus needs no Restart bus');
|
|
assert.deepEqual(await f.b.evaluate(`window.MosaicOrg.list.then(rows=>rows.map(row=>[row.id,row.busHealthy]))`), [['alpha', true], ['beta', true]]);
|
|
await f.shots('e1-restored-started', '#org-archive-success');
|
|
});
|
|
|
|
test('E1 (P1-4): a bus that does not start after a good restore is said so, and Restart bus starts it', { timeout: 45000 }, async t => {
|
|
const health = { up: false }, bus = startingBus(health, 1);
|
|
const f = await fixture(t, ['alpha', 'beta'], { health, bus, trail: true }); await archiveBeta(f);
|
|
await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`!document.querySelector('#org-archive-success').hidden`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'active', 'the restore stands');
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-archive-success').innerText`), /^Organization restored\. The bus did not start: bus failed to start; see the bus log/);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('[data-restore-bus]').textContent`), 'Restart bus');
|
|
await f.shots('e1-restored-bus-failed', '#org-archive-success');
|
|
await f.b.evaluate(`document.querySelector('[data-restore-bus]').click()`);
|
|
await f.wait(`/It answers/.test(document.querySelector('[data-restore-bus-receipt]')?.textContent || '')`);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('[data-restore-bus-receipt]').textContent`), 'Bus started. It answers.');
|
|
assert.equal(await f.b.evaluate(`document.querySelector('[data-restore-bus]').disabled`), true);
|
|
assert.deepEqual(bus.starts.map(([id]) => id), ['beta', 'beta']);
|
|
await f.shots('e1-restored-bus-restarted', '#org-archive-success');
|
|
});
|
|
|
|
test('E1 (P1-4): restoring the only organization reloads into it and still shows the restore and bus receipt', { timeout: 45000 }, async t => {
|
|
const health = { up: false }, bus = startingBus(health);
|
|
const f = await fixture(t, ['alpha'], { health, bus, trail: true }); await f.open('alpha');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!window.MosaicOrg && document.querySelector('#org-open-id')?.value === 'alpha'`);
|
|
await f.open('alpha'); await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`window.MosaicOrg?.id === 'alpha' && document.querySelector('#org-archive-success')?.hidden === false`);
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-archive-success').innerText`), /^Organization restored\. Bus started\. It answers\./);
|
|
assert.deepEqual(bus.starts.map(([id]) => id), ['alpha']);
|
|
// The kept receipt is shown once: a later visit does not repeat it.
|
|
assert.equal(await f.b.evaluate(`Object.keys(sessionStorage).filter(k=>k.startsWith('mosaic-org-restored:')).length`), 0);
|
|
});
|
|
|
|
test('E1 (P1-5): after restoring another organization, its dashboard shows the bus running and its organization file', { timeout: 45000 }, async t => {
|
|
const health = { up: false }, bus = startingBus(health);
|
|
const extra = id => ({
|
|
reads: { business: async () => ({ business: { id, human: 'local-dev-owner', arbiters: [], projects: [], vars: {}, instances: [] } }), settings: async () => ({}), queue: async () => ({ rows: [] }), busStatus: async () => ({}) },
|
|
bus: { instances: async () => ({ business: id, revision: 'a'.repeat(64), launch: true, noHire: [], retired: [], definitions: {}, instances: {} }), messages: async () => ({ messages: [], unread: 0 }), agents: async () => [], 'thread.claims': async () => [], inbox: async () => [], tasks: async () => [] },
|
|
});
|
|
const f = await fixture(t, ['alpha', 'beta'], { health, bus, trail: true, extra }); await archiveBeta(f);
|
|
await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`/Bus started\. It answers\./.test(document.querySelector('#org-archive-success')?.innerText || '')`);
|
|
await f.wait(`!!document.querySelector('#workspace-nav [data-workspace-org=beta]')`);
|
|
await f.b.evaluate(`location.hash = '#/orgs/beta/organization'`);
|
|
await f.wait(`/bus running, read/.test(document.querySelector('[data-org-dashboard]')?.innerText || '')`);
|
|
assert.match(await f.b.evaluate(`document.querySelector('[data-org-dashboard] [data-org-setting="Identity"] .v').textContent`), / · beta · /);
|
|
assert.deepEqual(bus.starts.map(([id]) => id), ['beta']);
|
|
});
|
|
|
|
test('restore does not announce success for an unexpected response', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
await f.b.evaluate(`const original=fetch.bind(window);window.fetch=(input,init)=>String(input).endsWith('/restore')?Promise.resolve(new Response(JSON.stringify({outcome:'done',step:'archive'}),{status:200})):original(input,init);document.querySelector('#org-restore').click()`);
|
|
await f.wait(`!!document.querySelector('#org-archive-error').textContent`);
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-archive-error').textContent`), /outcome is unknown/i);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-archive-success').hidden`), true);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'archived');
|
|
});
|
|
|
|
|
|
test('archiving the last active organization shows the no-organization state', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t, ['alpha']); await f.open('alpha');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!window.MosaicOrg && document.querySelector('#org-open-id')?.value === 'alpha'`);
|
|
assert.equal((await registry.listOrgs(f.ctx))[0].state, 'archived');
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-open-id').selectedOptions[0].textContent`), /archived/);
|
|
await f.open('alpha'); await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`window.MosaicOrg?.id === 'alpha'`);
|
|
assert.equal((await registry.listOrgs(f.ctx))[0].state, 'active');
|
|
});
|
|
|
|
test('fresh archived organization page offers owner restore while writes stay refused', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
// #1873 SP1: another organization's address no longer reloads the page, so a fresh page is loaded on purpose.
|
|
await f.b.navigate('about:blank');
|
|
await f.b.navigate(`http://127.0.0.1:${f.port}/#/orgs/beta/settings/organizations?org=beta`);
|
|
await f.wait(`document.querySelector('#org-archived') && !document.querySelector('#org-archived').hidden`);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-restore').disabled`), false);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-delete-open').disabled`), false);
|
|
assert.match(await f.b.evaluate(`document.querySelector('#org-archived').innerText`), /mosaic org restore beta --if-revision \d+/);
|
|
const refused = await f.b.evaluate(`Promise.all(['/api/orgs/beta/chat/projects','/api/orgs/beta/chat/threads/00000000-0000-0000-0000-000000000000/messages'].map(url=>fetch(url,{method:'POST',headers:{'content-type':'application/json'},body:'{}'}).then(r=>r.status)))`);
|
|
assert.deepEqual(refused, [409, 409]);
|
|
await f.b.evaluate(`document.querySelector('#org-restore').click()`);
|
|
await f.wait(`!document.querySelector('#org-archive-success').hidden`);
|
|
assert.equal((await registry.listOrgs(f.ctx)).find(e => e.id === 'beta').state, 'active');
|
|
});
|
|
|
|
// #1873 SP1: opening the archived organization's page in place (no reload) shows its restore command.
|
|
test('an archived organization opened in place shows its restore command', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
await f.b.evaluate(`window.__sameDocument = true; location.hash = '#/orgs/beta/settings/organizations?org=beta'`);
|
|
await f.wait(`window.MosaicOrg?.id === 'beta' && /mosaic org restore beta --if-revision \\d+/.test(document.querySelector('#org-archived')?.innerText ?? '')`);
|
|
assert.equal(await f.b.evaluate(`window.__sameDocument`), true);
|
|
});
|
|
|
|
test('owner can start a down bus after setup recorded done and sees recovery guidance on refusal', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t);
|
|
await f.b.evaluate(`(()=>{const old=window.fetch;window.fetch=async (input,init={})=>{const u=String(input);if(u.endsWith('/setup') && (!init.method || init.method==='GET'))return new Response(JSON.stringify({notifications:{state:'done'},queue:{state:'done'},projects:[],bus:{state:'done',verb:'mosaic bus start alpha'}}),{status:200});if(u.endsWith('/setup/bus') && init.method==='POST')return new Response(JSON.stringify({error:'stale-lock',message:'stale bus host lock'}),{status:409});return old(input,init)}})()`);
|
|
await f.open('alpha');
|
|
// The setup steps render after the editor title, once the setup and organization list reads answer.
|
|
await f.wait(`!!document.querySelector('[data-step="bus"] [data-run="bus"]:not(:disabled)')`);
|
|
await f.b.evaluate(`document.querySelector('[data-step="bus"] [data-run="bus"]').click()`);
|
|
await f.wait(`document.querySelector('[data-step="bus"]').innerText.includes('stale bus host lock')`);
|
|
assert.match(await f.b.evaluate(`document.querySelector('[data-step="bus"]').innerText`), /mosaic bus recover alpha/);
|
|
});
|
|
|
|
// E2E5 review P2: the list read by Refresh, not only opening the organization, decides whether Start shows.
|
|
test('an open Settings page offers Start when a refreshed org list reads the bus down after setup recorded done', { timeout: 45000 }, async t => {
|
|
const health = { up: true };
|
|
const f = await fixture(t, ['alpha', 'beta'], { health });
|
|
await f.b.evaluate(`(()=>{const old=window.fetch;window.fetch=async (input,init={})=>{const u=String(input);if(u.endsWith('/setup') && (!init.method || init.method==='GET'))return new Response(JSON.stringify({notifications:{state:'done'},queue:{state:'done'},projects:[],bus:{state:'done',verb:'mosaic bus start alpha'}}),{status:200});return old(input,init);};})()`);
|
|
await f.open('alpha');
|
|
await f.wait(`document.querySelector('[data-step="bus"]')?.textContent.includes('Done')`);
|
|
assert.equal(await f.b.evaluate(`!!document.querySelector('[data-step="bus"] [data-run="bus"]')`), false);
|
|
health.up = false;
|
|
await f.b.evaluate(`document.querySelector('#refresh').click()`);
|
|
await f.wait(`!!document.querySelector('[data-step="bus"] [data-run="bus"]:not(:disabled)')`);
|
|
health.up = true;
|
|
await f.b.evaluate(`document.querySelector('#refresh').click()`);
|
|
await f.wait(`!document.querySelector('[data-step="bus"] [data-run="bus"]')`);
|
|
});
|
|
|
|
for (const capability of [{ user:true, orgSetup:false }, { tester:true, orgSetup:false }, { orgSetup:false }]) {
|
|
test(`archived restore stays disabled without owner capability ${JSON.stringify(capability)}`, { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()`);
|
|
await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
await f.b.call('Page.addScriptToEvaluateOnNewDocument', {source:`{const old=window.fetch;window.fetch=(input,init)=>input instanceof URL && input.pathname==='/api/config'?Promise.resolve(new Response(JSON.stringify(${JSON.stringify(capability)}),{status:200})):old(input,init)}`});
|
|
// #1873 SP1: the stub applies to a new document; another organization's address alone no longer reloads.
|
|
await f.b.navigate('about:blank');
|
|
await f.b.navigate(`http://127.0.0.1:${f.port}/#/orgs/beta/settings/organizations?org=beta`);
|
|
await f.wait(`document.querySelector('#org-archived') && !document.querySelector('#org-archived').hidden`);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-restore').disabled`),true);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-delete-open').disabled`),true);
|
|
});
|
|
}
|
|
|
|
test('archive displays progress while its request is in flight', { timeout: 45000 }, async t => {
|
|
const f = await fixture(t); await f.open('beta');
|
|
await f.b.evaluate(`{const old=window.fetch;window.fetch=(input,init)=>String(input).endsWith('/archive')?new Promise(resolve=>window.releaseArchive=()=>old(input,init).then(resolve)):old(input,init);document.querySelector('#org-archive-open').click();document.querySelector('#org-archive-final').click()}`);
|
|
assert.equal(await f.b.evaluate(`document.querySelector('#org-archive-final').textContent`),'Archiving…');
|
|
await f.b.evaluate(`window.releaseArchive()`); await f.wait(`!document.querySelector('#org-archived').hidden`);
|
|
});
|