mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
356 lines
20 KiB
JavaScript
356 lines
20 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { once } from 'node:events';
|
|
import test from 'node:test';
|
|
import { chromium } from '@playwright/test';
|
|
import { createConsoleServer } from '../../src/console/server.mjs';
|
|
|
|
const views = [
|
|
['overview', 'Overview'],
|
|
['owner-briefing', 'Briefing'],
|
|
['flow-watch', 'Flow watch'],
|
|
['work', 'Work'],
|
|
['initiative-progress', 'Progress'],
|
|
['fleet', 'Fleet'],
|
|
['communications', 'Conversations'],
|
|
['organization', 'Organization'],
|
|
['evidence', 'Evidence'],
|
|
['delivery', 'Release gates'],
|
|
];
|
|
|
|
async function openDemo(t, viewport) {
|
|
const server = createConsoleServer();
|
|
server.listen(0, '127.0.0.1');
|
|
await once(server, 'listening');
|
|
let browser;
|
|
t.after(async () => {
|
|
await browser?.close();
|
|
await new Promise((resolve) => server.close(resolve));
|
|
});
|
|
browser = await chromium.launch({ headless: true });
|
|
const context = await browser.newContext({ viewport });
|
|
const page = await context.newPage();
|
|
await page.clock.install({ time: new Date('2026-10-01T12:00:00.000Z') });
|
|
await page.goto(`http://127.0.0.1:${server.address().port}/console?demo=1#work`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
return { page, context };
|
|
}
|
|
|
|
async function assertSelectedView(page, route, title) {
|
|
const selected = await page.locator('.console-workspace > [data-console-view]:not([hidden])').evaluateAll((sections) =>
|
|
[...new Set(sections.map((section) => section.dataset.consoleView))]);
|
|
assert.deepEqual(selected, [route], `only the ${route} view should be visible`);
|
|
assert.equal(await page.title(), route === 'overview' ? 'TORCH Dashboard' : `${title} | TORCH Local Fleet Console`);
|
|
assert.equal(await page.locator(`.console-rail a[aria-current="page"][href="#${route}"]`).count(), 1);
|
|
assert.equal(await page.locator('#project-name').isVisible(), true);
|
|
assert.equal(await page.locator('#snapshot-time').isVisible(), true);
|
|
}
|
|
|
|
test('SCN-console-view-title-mapping: Overview retains its dashboard title while other selected views stay contextual', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
const base = `http://127.0.0.1:${new URL(page.url()).port}/console?demo=1`;
|
|
await page.goto(`${base}#overview`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertSelectedView(page, 'overview', 'Overview');
|
|
|
|
for (const [route, title] of views.filter(([route]) => route !== 'overview')) {
|
|
await page.locator(`.console-rail a[href="#${route}"]`).click();
|
|
await assertSelectedView(page, route, title);
|
|
}
|
|
await page.locator('.console-rail a[href="#overview"]').click();
|
|
await assertSelectedView(page, 'overview', 'Overview');
|
|
});
|
|
|
|
test('SCN-console-owner-briefing-evidence-limitation-stays-visible: collapsed provenance keeps the warning visible', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
await page.locator('.console-rail a[href="#owner-briefing"]').click();
|
|
const publication = page.locator('#owner-briefing-content .briefing-publication');
|
|
const warning = publication.locator(':scope > p.briefing-muted').filter({ hasText: 'Recorded evidence, not independent live verification.' });
|
|
const provenance = publication.locator('details.briefing-provenance').filter({ hasText: 'Publication time and reporting window' });
|
|
assert.equal(await warning.count(), 1);
|
|
assert.equal(await warning.isVisible(), true);
|
|
assert.match(await page.locator('#owner-briefing').innerText(), /not independent live verification/);
|
|
assert.equal(await provenance.count(), 1);
|
|
assert.equal(await provenance.evaluate((details) => details.open), false);
|
|
assert.equal(await provenance.locator('time').count(), 1, 'the exact publication time remains in compact metadata');
|
|
assert.equal(await provenance.locator('time').isVisible(), false, 'publication time stays inside the closed disclosure');
|
|
});
|
|
|
|
test('SCN-console-secondary-deeplink-focus-and-history: direct fragments and history land on the actual target', async (t) => {
|
|
const { page } = await openDemo(t, { width: 390, height: 844 });
|
|
const base = `http://127.0.0.1:${new URL(page.url()).port}/console?demo=1`;
|
|
const assertTarget = async (id, route, title) => {
|
|
await assertSelectedView(page, route, title);
|
|
const state = await page.locator(`#${id}`).evaluate((target) => {
|
|
const bounds = target.getBoundingClientRect();
|
|
return {
|
|
activeId: document.activeElement?.id,
|
|
top: bounds.top,
|
|
bottom: bounds.bottom,
|
|
height: innerHeight,
|
|
};
|
|
});
|
|
assert.equal(state.activeId, id, `the ${id} target must receive focus`);
|
|
assert.ok(state.top < state.height && state.bottom > 0, `${id} must intersect the viewport: ${JSON.stringify(state)}`);
|
|
};
|
|
|
|
await page.goto(`${base}#organization-proposals`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertTarget('organization-proposals', 'organization', 'Organization');
|
|
await page.goto(`${base}#approval-request-list`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertTarget('approval-request-list', 'flow-watch', 'Flow watch');
|
|
await page.goBack();
|
|
await assertTarget('organization-proposals', 'organization', 'Organization');
|
|
await page.goForward();
|
|
await assertTarget('approval-request-list', 'flow-watch', 'Flow watch');
|
|
});
|
|
|
|
test('SCN-console-operational-deeplink-layout-history-and-refresh: Fleet deep links settle after first render without stealing later navigation', async (t) => {
|
|
for (const viewport of [{ width: 1280, height: 900 }, { width: 390, height: 844 }]) {
|
|
const { page } = await openDemo(t, viewport);
|
|
const base = `http://127.0.0.1:${new URL(page.url()).port}/console?demo=1`;
|
|
const assertManagerWakes = async () => {
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
const state = await page.locator('#manager-wakes').evaluate((target) => {
|
|
const bounds = target.getBoundingClientRect();
|
|
return {
|
|
activeId: document.activeElement?.id,
|
|
operationsOpen: target.closest('#fleet-operations')?.open,
|
|
top: bounds.top,
|
|
bottom: bounds.bottom,
|
|
height: innerHeight,
|
|
};
|
|
});
|
|
assert.equal(state.operationsOpen, true);
|
|
assert.equal(state.activeId, 'manager-wakes');
|
|
assert.ok(state.top >= 0 && state.bottom <= state.height,
|
|
`manager-wakes must be fully inside ${viewport.width}x${viewport.height}: ${JSON.stringify(state)}`);
|
|
};
|
|
|
|
await page.goto(`${base}#manager-wakes`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertManagerWakes();
|
|
|
|
await page.goto(`${base}#overview`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await page.locator('#attention-list a[href="#manager-wakes"]').click();
|
|
await assertManagerWakes();
|
|
await page.goBack();
|
|
await assertSelectedView(page, 'overview', 'Overview');
|
|
await page.goForward();
|
|
await assertManagerWakes();
|
|
|
|
await page.locator('.console-rail a[href="#work"]').click();
|
|
const beforeRefreshRender = await page.evaluate(() => {
|
|
window.scrollTo({ top: 347, behavior: 'instant' });
|
|
return window.scrollY;
|
|
});
|
|
assert.equal(beforeRefreshRender, 347, 'the deterministic scroll stimulus must establish its exact nonzero baseline');
|
|
await page.locator('#refresh-console').evaluate((button) => {
|
|
button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
});
|
|
await page.locator('#live-refresh-status[data-generation="2"]').waitFor();
|
|
assert.equal(new URL(page.url()).hash, '#work');
|
|
await assertSelectedView(page, 'work', 'Work');
|
|
assert.notEqual(await page.evaluate(() => document.activeElement?.id), 'manager-wakes');
|
|
assert.equal(await page.evaluate(() => scrollY), beforeRefreshRender,
|
|
'a later refresh must retain the exact pre-refresh user scroll position');
|
|
}
|
|
});
|
|
|
|
test('SCN-console-priority-preview-invalidates-on-newer-evidence: refresh preserves editable drafts and rejects the old preview', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
const form = page.locator('.priority-change-form[data-task-id="API-08"]');
|
|
const ticket = form.locator('xpath=../..');
|
|
await ticket.locator('.priority-change > summary').click();
|
|
await form.locator('[name="priority"]').selectOption('urgent');
|
|
await form.locator('[name="reason"]').fill('Preserve this unsent explanation across a newer task revision.');
|
|
await form.getByRole('button', { name: 'Review change', exact: true }).click();
|
|
await form.locator('[data-priority-confirm]').waitFor();
|
|
const revisionN = Number(await form.getAttribute('data-revision'));
|
|
const staleToken = await form.getAttribute('data-preview-token');
|
|
assert.equal(await form.locator('[data-priority-preview] dd').nth(2).innerText(), String(revisionN));
|
|
|
|
const revisionN1 = await page.evaluate(async (expectedRevision) => {
|
|
const path = '/api/backlog/tasks/API-08/priority';
|
|
const change = { priority: 'low', reason: 'Separate normal sample API action advances authoritative evidence.', expectedRevision };
|
|
const preview = await globalThis.TorchConsoleDemo.request(`${path}/preview`, change);
|
|
const applied = await globalThis.TorchConsoleDemo.request(path, { ...change, token: preview.token, planHash: preview.planHash });
|
|
return applied.task.revision;
|
|
}, revisionN);
|
|
assert.equal(revisionN1, revisionN + 1);
|
|
|
|
await page.locator('#refresh-console').click();
|
|
await page.locator('#live-refresh-status[data-generation="2"]').waitFor();
|
|
assert.equal(Number(await form.getAttribute('data-revision')), revisionN1);
|
|
assert.equal(await ticket.locator('.priority').textContent(), 'low');
|
|
assert.equal(await form.getAttribute('data-preview-token'), null);
|
|
assert.equal(await form.locator('[data-priority-preview]').isVisible(), false);
|
|
assert.equal(await form.locator('[data-priority-confirm]').count(), 0);
|
|
assert.equal(await form.locator('[name="priority"]').inputValue(), 'urgent');
|
|
assert.equal(await form.locator('[name="reason"]').inputValue(), 'Preserve this unsent explanation across a newer task revision.');
|
|
assert.equal(await form.locator('[name="priority"]').isDisabled(), false);
|
|
assert.equal(await form.locator('[name="reason"]').isDisabled(), false);
|
|
assert.match(await form.locator('.priority-change-status').innerText(), new RegExp(`revision ${revisionN1}`));
|
|
|
|
await page.locator('.console-rail a[href="#overview"]').click();
|
|
await page.locator('.console-rail a[href="#work"]').click();
|
|
await page.locator('#refresh-console').click();
|
|
await page.locator('#live-refresh-status[data-generation="3"]').waitFor();
|
|
const preservedForm = page.locator('.priority-change-form[data-task-id="API-08"]');
|
|
const preservedTicket = preservedForm.locator('xpath=../..');
|
|
assert.equal(Number(await preservedForm.getAttribute('data-revision')), revisionN1);
|
|
assert.equal(await preservedTicket.locator('.priority').textContent(), 'low');
|
|
assert.equal(await preservedForm.getAttribute('data-preview-token'), null);
|
|
assert.equal(await preservedForm.locator('[data-priority-preview]').isVisible(), false);
|
|
assert.equal(await preservedForm.locator('[data-priority-confirm]').count(), 0);
|
|
assert.equal(await preservedForm.locator('[name="priority"]').inputValue(), 'urgent');
|
|
assert.equal(await preservedForm.locator('[name="reason"]').inputValue(), 'Preserve this unsent explanation across a newer task revision.');
|
|
assert.equal(await preservedForm.locator('[name="priority"]').isDisabled(), false);
|
|
assert.equal(await preservedForm.locator('[name="reason"]').isDisabled(), false);
|
|
|
|
await preservedForm.getByRole('button', { name: 'Review change', exact: true }).click();
|
|
await preservedForm.locator('[data-priority-confirm]').waitFor();
|
|
const currentToken = await preservedForm.getAttribute('data-preview-token');
|
|
assert.notEqual(currentToken, staleToken);
|
|
assert.equal(await preservedForm.locator('[data-priority-preview] dd').nth(2).innerText(), String(revisionN1));
|
|
await page.locator('#refresh-console').click();
|
|
await page.locator('#live-refresh-status[data-generation="4"]').waitFor();
|
|
const currentForm = page.locator('.priority-change-form[data-task-id="API-08"]');
|
|
assert.equal(await currentForm.getAttribute('data-preview-token'), currentToken);
|
|
assert.equal(await currentForm.locator('[data-priority-preview]').isVisible(), true);
|
|
assert.equal(await currentForm.locator('[data-priority-confirm]').isVisible(), true);
|
|
assert.equal(await currentForm.locator('[name="priority"]').inputValue(), 'urgent');
|
|
assert.equal(await currentForm.locator('[name="reason"]').inputValue(), 'Preserve this unsent explanation across a newer task revision.');
|
|
});
|
|
|
|
test('SCN-console-current-preview-and-draft-survive-unchanged-refresh: a current preview and its fields remain intact', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
const form = page.locator('.priority-change-form[data-task-id="API-08"]');
|
|
const ticket = form.locator('xpath=../..');
|
|
await ticket.locator('.priority-change > summary').click();
|
|
await form.locator('[name="priority"]').selectOption('urgent');
|
|
await form.locator('[name="reason"]').fill('Keep this current unsent explanation after an unchanged refresh.');
|
|
await form.getByRole('button', { name: 'Review change', exact: true }).click();
|
|
await form.locator('[data-priority-confirm]').waitFor();
|
|
const revision = await form.getAttribute('data-revision');
|
|
const token = await form.getAttribute('data-preview-token');
|
|
|
|
await page.locator('#refresh-console').click();
|
|
await page.locator('#live-refresh-status[data-generation="2"]').waitFor();
|
|
assert.equal(await form.getAttribute('data-revision'), revision);
|
|
assert.equal(await form.getAttribute('data-preview-token'), token);
|
|
assert.equal(await form.locator('[data-priority-preview]').isVisible(), true);
|
|
assert.equal(await form.locator('[data-priority-confirm]').isVisible(), true);
|
|
assert.equal(await form.locator('[name="priority"]').inputValue(), 'urgent');
|
|
assert.equal(await form.locator('[name="reason"]').inputValue(), 'Keep this current unsent explanation after an unchanged refresh.');
|
|
});
|
|
|
|
test('SCN-console-view-navigation: fragments, direct loads, Back and Forward select one addressable view', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
await assertSelectedView(page, 'work', 'Work');
|
|
|
|
await page.locator('.console-rail a[href="#fleet"]').click();
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
await page.locator('.console-rail a[href="#evidence"]').click();
|
|
await assertSelectedView(page, 'evidence', 'Evidence');
|
|
await page.goBack();
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
await page.goForward();
|
|
await assertSelectedView(page, 'evidence', 'Evidence');
|
|
|
|
await page.goto(`http://127.0.0.1:${new URL(page.url()).port}/console?demo=1#approval-request-list`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertSelectedView(page, 'flow-watch', 'Flow watch');
|
|
});
|
|
|
|
test('SCN-console-operational-deeplink-fleet: manager-wakes belongs to Fleet across direct loads and history', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
const base = `http://127.0.0.1:${new URL(page.url()).port}/console?demo=1`;
|
|
const assertManagerWakesTarget = async () => {
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
assert.equal(await page.locator('#fleet-operations').isVisible(), true);
|
|
assert.equal(await page.locator('#fleet-operations').evaluate((details) => details.open), true,
|
|
'the Operations disclosure must open for its nested target');
|
|
const target = await page.locator('#manager-wakes').evaluate((element) => {
|
|
const bounds = element.getBoundingClientRect();
|
|
return { activeId: document.activeElement?.id, top: bounds.top, bottom: bounds.bottom, height: innerHeight };
|
|
});
|
|
assert.equal(target.activeId, 'manager-wakes', 'the actual fragment target must receive focus');
|
|
assert.ok(target.top < target.height && target.bottom > 0,
|
|
`manager-wakes must intersect the viewport: ${JSON.stringify(target)}`);
|
|
};
|
|
|
|
await page.goto(`${base}#manager-wakes`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await assertManagerWakesTarget();
|
|
|
|
await page.locator('.console-rail a[href="#fleet"]').click();
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
await page.goBack();
|
|
await assertManagerWakesTarget();
|
|
await page.goForward();
|
|
await assertSelectedView(page, 'fleet', 'Fleet');
|
|
|
|
await page.goto(`${base}#overview`);
|
|
await page.locator('#live-refresh-status[data-generation="1"]').waitFor();
|
|
await page.locator('#attention-list a[href="#manager-wakes"]').click();
|
|
await assertManagerWakesTarget();
|
|
await page.goBack();
|
|
await assertSelectedView(page, 'overview', 'Overview');
|
|
await page.goForward();
|
|
await assertManagerWakesTarget();
|
|
|
|
await page.locator('.console-rail a[href="#work"]').click();
|
|
await assertSelectedView(page, 'work', 'Work');
|
|
assert.equal(await page.locator('#fleet-operations').isVisible(), false,
|
|
'operational panels must be hidden outside the selected Fleet view');
|
|
});
|
|
|
|
test('SCN-console-mobile-navigation: every destination is visible at 390x844 without horizontal hunting', async (t) => {
|
|
const { page } = await openDemo(t, { width: 390, height: 844 });
|
|
assert.equal(await page.locator('.console-rail a').count(), views.length);
|
|
|
|
for (const [route, title] of views) {
|
|
const link = page.locator(`.console-rail a[href="#${route}"]`);
|
|
assert.equal(await link.isVisible(), true, `${title} navigation must remain visible`);
|
|
await link.click();
|
|
await assertSelectedView(page, route, title);
|
|
const widths = await page.evaluate(() => ({
|
|
viewport: innerWidth,
|
|
document: document.documentElement.scrollWidth,
|
|
navigation: document.querySelector('.console-rail').scrollWidth,
|
|
navigationClient: document.querySelector('.console-rail').clientWidth,
|
|
}));
|
|
assert.ok(widths.document <= widths.viewport, `document overflows horizontally: ${JSON.stringify(widths)}`);
|
|
assert.ok(widths.navigation <= widths.navigationClient, `navigation requires horizontal scrolling: ${JSON.stringify(widths)}`);
|
|
}
|
|
});
|
|
|
|
test('SCN-console-view-drafts: navigation and refresh retain unsent edits and the explicit owner preview boundary', async (t) => {
|
|
const { page } = await openDemo(t, { width: 1280, height: 900 });
|
|
const titleInput = page.locator('#task-create-form [name="title"]');
|
|
await page.locator('.task-create-panel > summary').click();
|
|
await titleInput.fill('Unsent route-change draft');
|
|
await page.locator('.console-rail a[href="#flow-watch"]').click();
|
|
const approval = page.locator('.approval-decision-form').first();
|
|
await approval.locator('[name="decision"]').selectOption('approved');
|
|
await approval.locator('[name="note"]').fill('Review remains explicit after navigation.');
|
|
await approval.locator('[data-approval-preview]').click();
|
|
await approval.locator('[data-approval-confirm]:visible').waitFor();
|
|
const previewToken = await approval.getAttribute('data-preview-token');
|
|
|
|
await page.locator('.console-rail a[href="#work"]').click();
|
|
await page.locator('#refresh-console').click();
|
|
await page.locator('#live-refresh-status[data-generation="2"]').waitFor();
|
|
assert.equal(await titleInput.inputValue(), 'Unsent route-change draft');
|
|
|
|
await page.locator('.console-rail a[href="#flow-watch"]').click();
|
|
assert.equal(await approval.getAttribute('data-preview-token'), previewToken);
|
|
assert.equal(await approval.locator('[data-approval-confirm]').isVisible(), true);
|
|
assert.equal(await approval.locator('[name="decision"]').isDisabled(), true);
|
|
assert.equal(await approval.locator('[data-approval-edit]').isVisible(), true);
|
|
assert.match(await approval.locator('.approval-decision-status').innerText(), /bound to this request revision/);
|
|
assert.equal(await page.locator('#approval-request-count').innerText(), '1 open');
|
|
});
|