feat: add highlighted protocol views and inline operation forms
Reference site / build (push) Canceled after 0s
Reference site / build (push) Canceled after 0s
Expose Rust Text View tokens and safely highlight signatures and JSON results. Add animated Interface and inline Run disclosures with a shared 10ms line cadence, typed argument inputs, and explicit execution. Update documentation and browser regressions for the new controls and explicit counter-value responses.
This commit is contained in:
@@ -14,7 +14,7 @@ async function descriptor(page: Page) {
|
||||
await expect(page.getByLabel('Object Text View')).toContainText('object {');
|
||||
await page.locator('[data-interface-toggle]').first().click();
|
||||
await expect(page.getByLabel('Interface Text View')).toContainText('interface');
|
||||
await page.getByLabel('Operation', { exact: true }).selectOption('read');
|
||||
await page.getByRole('button', { name: 'Run read', exact: true }).click();
|
||||
}
|
||||
|
||||
async function traceObserves(page: Page) {
|
||||
@@ -60,6 +60,39 @@ for (const [route, path, name] of [
|
||||
});
|
||||
}
|
||||
|
||||
for (const colorScheme of ['light', 'dark'] as const) {
|
||||
test(`Interface buttons use a text-width TUI background in ${colorScheme} mode`, async ({ page }, info) => {
|
||||
await page.emulateMedia({ colorScheme });
|
||||
await page.goto(protocolURL(info.project.use.baseURL!, 'playground/')); await ready(page);
|
||||
const button = page.getByRole('button', { name: '? /::collection', exact: true });
|
||||
const toggle = page.locator('[data-interface-toggle]').filter({ hasText: '/::collection' });
|
||||
await expect(button).toBeVisible();
|
||||
const background = await button.evaluate(element => {
|
||||
const probe = document.createElement('span'); probe.style.backgroundColor = 'var(--code-bg)';
|
||||
element.append(probe); const color = getComputedStyle(probe).backgroundColor; probe.remove(); return color;
|
||||
});
|
||||
expect(background).not.toBe('rgba(0, 0, 0, 0)');
|
||||
for (const state of ['?', 'v', '>']) {
|
||||
await page.mouse.move(0, 0);
|
||||
await page.getByRole('button', { name: 'Read', exact: true }).focus();
|
||||
await expect(toggle).toHaveText(`${state} /::collection`);
|
||||
await expect(toggle).toHaveCSS('background-color', background);
|
||||
await expect(toggle).toHaveCSS('border-radius', '0px');
|
||||
await expect(toggle).toHaveCSS('border-top-width', '0px');
|
||||
await expect(toggle).toHaveCSS('padding-top', '0px');
|
||||
expect(await toggle.evaluate(element => element.getBoundingClientRect().width < element.parentElement!.getBoundingClientRect().width)).toBe(true);
|
||||
await toggle.hover();
|
||||
await expect(toggle).not.toHaveCSS('background-color', background);
|
||||
if (state !== '>') await toggle.click();
|
||||
}
|
||||
await page.mouse.move(0, 0); await toggle.focus(); await page.keyboard.press('Enter');
|
||||
await expect(toggle).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(toggle).toHaveCSS('outline-width', '2px');
|
||||
await expect(page.getByLabel('Interface Text View').filter({ visible: true })).toContainText('/::collection');
|
||||
await page.screenshot({ path: info.outputPath(`interface-background-${colorScheme}.png`), fullPage: true });
|
||||
});
|
||||
}
|
||||
|
||||
test('Object selection shows an unboxed Text View without Observe forms or step panels', async ({ page }, info) => {
|
||||
await page.goto(protocolURL(info.project.use.baseURL!, 'sources/')); await ready(page);
|
||||
const object = page.getByLabel('Object Text View');
|
||||
@@ -70,16 +103,20 @@ test('Object selection shows an unboxed Text View without Observe forms or step
|
||||
await expect(object).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)');
|
||||
await expect(panel(page).locator('fieldset, legend, h2, h3, [data-protocol-path], [data-protocol-depth], [data-protocol-observe], [data-protocol-stats], [data-protocol-children]')).toHaveCount(0);
|
||||
await expect(page.locator('[data-protocol-status]')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await expect(page.getByLabel('Operation result')).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: '? /::document', exact: true })).toBeVisible();
|
||||
await page.screenshot({ path: info.outputPath('protocol-object.png'), fullPage: true });
|
||||
await page.getByRole('button', { name: '? /::document', exact: true }).click();
|
||||
await expect(page.getByLabel('Interface Text View')).toBeVisible();
|
||||
await expect(page.locator('[data-protocol-operation-controls]')).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
|
||||
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-protocol-call]')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: 'Run read', exact: true }).click();
|
||||
await expect(page.getByRole('form', { name: 'read arguments', exact: true })).toHaveAttribute('data-operation-form', 'read');
|
||||
await expect(page.getByRole('button', { name: 'Execute read', exact: true })).toBeEnabled();
|
||||
await expect(page.getByLabel('Operation result')).toBeHidden();
|
||||
await page.getByRole('button', { name: 'v /::document', exact: true }).click();
|
||||
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await expect(object).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -96,12 +133,12 @@ test('tree selection automatically observes Depth 1 only, including root, nested
|
||||
await expect(page.getByLabel('Interface Text View')).toBeHidden();
|
||||
await expect(panel(page)).toHaveAttribute('data-interfaces', '0');
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '0');
|
||||
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
|
||||
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
expected.push({ path: canonicalPath, depth: 1 });
|
||||
expect(await observedCalls(page)).toEqual(expected);
|
||||
}
|
||||
await page.locator('[data-interface-toggle]').first().click();
|
||||
await page.getByLabel('Operation', { exact: true }).selectOption('read');
|
||||
await page.getByRole('button', { name: 'Run read', exact: true }).click();
|
||||
await page.locator('[data-protocol-call]').click();
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '1');
|
||||
// Selecting the current Object resets the previous Interface and result.
|
||||
@@ -167,12 +204,12 @@ test('Interface clicks fetch unknown signatures and fold/reopen acquired ones wi
|
||||
const signature = await view.textContent();
|
||||
await expect(panel(page)).toHaveAttribute('data-interfaces', '1');
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '0');
|
||||
await expect(page.getByLabel('Operation', { exact: true })).toBeEnabled();
|
||||
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
|
||||
await toggle.focus(); await page.keyboard.press('Space');
|
||||
await expect(toggle).toHaveText('> /::document');
|
||||
await expect(toggle).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(view).toBeHidden();
|
||||
await expect(page.getByLabel('Operation', { exact: true })).toBeDisabled();
|
||||
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await toggle.click();
|
||||
await expect(view).toHaveText(signature!);
|
||||
await expect(panel(page)).toHaveAttribute('data-interfaces', '1');
|
||||
@@ -201,21 +238,21 @@ test('failed Interface acquisition remains unknown and cannot retain a previous
|
||||
await page.goto(protocolURL(info.project.use.baseURL!)); await ready(page);
|
||||
await page.locator('.demo-node[data-path="/"]').click();
|
||||
await page.getByRole('button', { name: '? /::document', exact: true }).click();
|
||||
await page.getByLabel('Operation', { exact: true }).selectOption('read');
|
||||
await page.getByRole('button', { name: 'Run read', exact: true }).click();
|
||||
const missing = page.getByRole('button', { name: `? /::${missingName}`, exact: true });
|
||||
await missing.click();
|
||||
await expect(missing).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(page.getByRole('button', { name: '> /::document', exact: true })).toBeVisible();
|
||||
await expect(page.locator('[data-protocol-status]')).toContainText('protocol');
|
||||
await expect(page.locator('[data-interface-view]:visible')).toHaveCount(0);
|
||||
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
|
||||
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await expect(page.getByLabel('Operation result')).toBeHidden();
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '0');
|
||||
await expect(page.locator('[data-protocol-interfaces] img')).toHaveCount(0);
|
||||
expect(await page.evaluate(() => '__interfaceExecuted' in window)).toBe(false);
|
||||
await page.getByRole('button', { name: '> /::document', exact: true }).click();
|
||||
await expect(page.locator('[data-interface-view]:visible')).toContainText('interface "/::document"');
|
||||
await expect(page.getByLabel('Operation', { exact: true })).toBeEnabled();
|
||||
await expect(page.getByRole('button', { name: 'Run read', exact: true })).toBeEnabled();
|
||||
});
|
||||
|
||||
test('Protocol observes automatically but acquires Interfaces and calls Operations only explicitly', async ({ page }, info) => {
|
||||
@@ -283,7 +320,7 @@ test('selection, history, reload and mode switches never replay an operation', a
|
||||
await ready(page);
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '1');
|
||||
await expect(page.getByLabel('Operation result')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
|
||||
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
}
|
||||
await page.goBack(); await ready(page);
|
||||
await expect(panel(page)).toHaveAttribute('data-path', '/sources');
|
||||
@@ -400,22 +437,25 @@ test('input and protocol errors remain visible, and tree selection clears stale
|
||||
await page.locator('.demo-node[data-path="/sources"]').click(); await ready(page);
|
||||
await expect(page.locator('[data-protocol-status]')).toBeVisible();
|
||||
await expect(page.locator('[data-protocol-status]')).toContainText('protocol');
|
||||
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
|
||||
await expect(page.locator('[data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await expect(page.getByLabel('Object Text View')).toBeHidden();
|
||||
await page.locator('.demo-node[data-path="/"]').click();
|
||||
await expect(panel(page)).toHaveAttribute('data-path', '/');
|
||||
await ready(page);
|
||||
await descriptor(page);
|
||||
await page.locator('[data-protocol-arguments]').fill('{broken');
|
||||
await page.locator('.demo-node[data-path="/playground"]').click(); await ready(page);
|
||||
await page.locator('.demo-node[data-path="/playground/apple"]').click(); await ready(page);
|
||||
await expect(panel(page)).toHaveAttribute('data-path', '/playground/apple');
|
||||
await page.getByRole('button', { name: '? /::counter', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run increment', exact: true }).click();
|
||||
await page.getByLabel('increment.amount', { exact: true }).fill('broken');
|
||||
await page.locator('[data-protocol-call]').click();
|
||||
await expect(page.locator('[data-protocol-status]')).toBeVisible();
|
||||
await expect(page.locator('[data-protocol-status]')).toContainText('input');
|
||||
await expect(panel(page)).toHaveAttribute('data-calls', '0');
|
||||
await page.locator('.demo-node[data-path="/book"]').click(); await ready(page);
|
||||
await expect(page.getByLabel('Object Text View')).toContainText('name: "book"');
|
||||
await expect(page.locator('[data-protocol-operation-controls]')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-operation-controls], [data-protocol-operation], [data-protocol-arguments], [data-operation-form], [data-protocol-call]')).toHaveCount(0);
|
||||
await expect(page.locator('[data-protocol-status]')).toBeHidden();
|
||||
await expect(page.locator('[data-protocol-call]')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('Text View and returned markup are displayed as text, including in dark mode', async ({ page }, info) => {
|
||||
|
||||
Reference in New Issue
Block a user