fix: simplify console mode controls with a compact footer toggle
This commit is contained in:
@@ -85,14 +85,14 @@ Deno.test("Console pending scopes are independent and match Tasks row typography
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
assertEquals(statusGeometry.typography, statusGeometry.taskTypography);
|
assertEquals(statusGeometry.typography, statusGeometry.taskTypography);
|
||||||
assertEquals(statusGeometry.height, 16);
|
assertEquals(statusGeometry.height, 24);
|
||||||
assertEquals(statusGeometry.belowComposer, true);
|
assertEquals(statusGeometry.belowComposer, true);
|
||||||
assertEquals(statusGeometry.alignedWithTasks, true);
|
assertEquals(statusGeometry.alignedWithTasks, true);
|
||||||
assertEquals(statusGeometry.visible, true);
|
assertEquals(statusGeometry.visible, true);
|
||||||
assertEquals(statusGeometry.wrap, "nowrap");
|
assertEquals(statusGeometry.wrap, "nowrap");
|
||||||
assertEquals(statusGeometry.pageOverflow, false);
|
assertEquals(statusGeometry.pageOverflow, false);
|
||||||
const details = metadata.getByRole("button", { name: "Details", exact: true });
|
const details = metadata.getByRole("button", { name: "Details", exact: true });
|
||||||
assertEquals(await page.locator(".console-header").getByRole("button", { name: "Details" }).count(), 0);
|
assertEquals(await page.locator(".console-header").count(), 0);
|
||||||
const detailsGeometry = await details.evaluate((button) => {
|
const detailsGeometry = await details.evaluate((button) => {
|
||||||
const row = button.closest(".worker-context-status")!;
|
const row = button.closest(".worker-context-status")!;
|
||||||
const rect = button.getBoundingClientRect();
|
const rect = button.getBoundingClientRect();
|
||||||
@@ -105,7 +105,49 @@ Deno.test("Console pending scopes are independent and match Tasks row typography
|
|||||||
});
|
});
|
||||||
assertEquals(detailsGeometry.rightAligned, true);
|
assertEquals(detailsGeometry.rightAligned, true);
|
||||||
assertEquals(detailsGeometry.centered, true);
|
assertEquals(detailsGeometry.centered, true);
|
||||||
assert(detailsGeometry.width > 0);
|
assertEquals(detailsGeometry.width, 24);
|
||||||
|
const overview = metadata.getByRole("switch", { name: "Overview", exact: true });
|
||||||
|
const controls = await overview.evaluate((group) => {
|
||||||
|
const row = group.closest(".worker-context-status")!;
|
||||||
|
const rowRect = row.getBoundingClientRect();
|
||||||
|
const buttons = [...row.querySelectorAll("button")];
|
||||||
|
return {
|
||||||
|
buttonsInsideRow: buttons.every((button) => {
|
||||||
|
const rect = button.getBoundingClientRect();
|
||||||
|
return rect.left >= rowRect.left && rect.right <= rowRect.right &&
|
||||||
|
rect.top >= rowRect.top && rect.bottom <= rowRect.bottom;
|
||||||
|
}),
|
||||||
|
heights: buttons.map((button) => button.getBoundingClientRect().height),
|
||||||
|
modeFont: getComputedStyle(buttons[0]).fontFamily,
|
||||||
|
divider: getComputedStyle(buttons[1]).borderLeftWidth,
|
||||||
|
beforeDetails: group.nextElementSibling?.getAttribute("aria-label") === "Details",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
assertEquals(controls.buttonsInsideRow, true);
|
||||||
|
assertEquals(controls.heights, [24, 24]);
|
||||||
|
assertEquals(controls.modeFont.includes("mono"), false);
|
||||||
|
assertEquals(controls.divider, "0px");
|
||||||
|
assertEquals(controls.beforeDetails, true);
|
||||||
|
const onSize = (await overview.boundingBox())!;
|
||||||
|
assert(onSize.width < 90, "Switch and fixed label should use less than 90px");
|
||||||
|
assertEquals(await overview.getAttribute("aria-checked"), "true");
|
||||||
|
await overview.focus();
|
||||||
|
assertEquals(await overview.evaluate((button) => getComputedStyle(button).outlineStyle), "solid");
|
||||||
|
await page.keyboard.press("Enter");
|
||||||
|
assertEquals(await overview.getAttribute("aria-checked"), "false");
|
||||||
|
assertEquals((await overview.textContent())!.trim(), "Overview");
|
||||||
|
assertEquals((await overview.boundingBox())!.width, onSize.width);
|
||||||
|
if (mode === "both") {
|
||||||
|
await Deno.mkdir(join(root, "target/web-ux/console-footer-switch"), { recursive: true });
|
||||||
|
await page.screenshot({ path: join(root, `target/web-ux/console-footer-switch/${colorScheme}-${width}-off.png`) });
|
||||||
|
}
|
||||||
|
await page.keyboard.press("Space");
|
||||||
|
assertEquals(await overview.getAttribute("aria-checked"), "true");
|
||||||
|
assertEquals((await overview.boundingBox())!.width, onSize.width);
|
||||||
|
await overview.locator("span").last().click();
|
||||||
|
assertEquals(await overview.getAttribute("aria-checked"), "false");
|
||||||
|
await overview.locator(".console-view-toggle-track").click();
|
||||||
|
assertEquals(await overview.getAttribute("aria-checked"), "true");
|
||||||
await details.focus();
|
await details.focus();
|
||||||
await page.keyboard.press("Enter");
|
await page.keyboard.press("Enter");
|
||||||
assertEquals(await details.getAttribute("aria-expanded"), "true");
|
assertEquals(await details.getAttribute("aria-expanded"), "true");
|
||||||
@@ -117,8 +159,8 @@ Deno.test("Console pending scopes are independent and match Tasks row typography
|
|||||||
await page.getByRole("button", { name: "Close", exact: true }).click();
|
await page.getByRole("button", { name: "Close", exact: true }).click();
|
||||||
assertEquals(await details.getAttribute("aria-expanded"), "false");
|
assertEquals(await details.getAttribute("aria-expanded"), "false");
|
||||||
if (mode === "both") {
|
if (mode === "both") {
|
||||||
await Deno.mkdir(join(root, "target/web-ux/composer-metadata"), { recursive: true });
|
await Deno.mkdir(join(root, "target/web-ux/console-footer-switch"), { recursive: true });
|
||||||
await page.screenshot({ path: join(root, `target/web-ux/composer-metadata/${colorScheme}-${width}.png`) });
|
await page.screenshot({ path: join(root, `target/web-ux/console-footer-switch/${colorScheme}-${width}.png`) });
|
||||||
}
|
}
|
||||||
const queue = mode === "both" || mode === "queue";
|
const queue = mode === "both" || mode === "queue";
|
||||||
const notifications = ["both", "notifications", "legacy"].includes(mode);
|
const notifications = ["both", "notifications", "legacy"].includes(mode);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
import type { ConsoleWorkerMetadata } from "./model";
|
import type { ConsoleWorkerMetadata } from "./model";
|
||||||
import {
|
import {
|
||||||
formatContextSummary,
|
formatContextSummary,
|
||||||
@@ -7,33 +8,39 @@
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
metadata: ConsoleWorkerMetadata | null;
|
metadata: ConsoleWorkerMetadata | null;
|
||||||
|
controls?: Snippet;
|
||||||
detailsOpen?: boolean;
|
detailsOpen?: boolean;
|
||||||
onToggleDetails?: () => void;
|
onToggleDetails?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
let { metadata, detailsOpen = false, onToggleDetails }: Props = $props();
|
let { metadata, controls, detailsOpen = false, onToggleDetails }: Props = $props();
|
||||||
const modelSummary = $derived(formatModelSummary(metadata));
|
const modelSummary = $derived(formatModelSummary(metadata));
|
||||||
const contextSummary = $derived(formatContextSummary(metadata));
|
const contextSummary = $derived(formatContextSummary(metadata));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="worker-context-status" aria-label="Worker model and context">
|
<div class="worker-context-status" aria-label="Worker model and context">
|
||||||
<span class="worker-model" title={modelSummary}>{modelSummary}</span>
|
<span class="worker-model" title={modelSummary}>{modelSummary}</span>
|
||||||
<span class="separator" aria-hidden="true">|</span>
|
<span class="separator" aria-hidden="true">·</span>
|
||||||
<span class="worker-context" title={contextSummary}>{contextSummary}</span>
|
<span class="worker-context" title={contextSummary}>{contextSummary}</span>
|
||||||
{#if onToggleDetails}
|
{#if controls || onToggleDetails}
|
||||||
<button
|
<div class="worker-context-actions">
|
||||||
type="button"
|
{@render controls?.()}
|
||||||
class="details-button"
|
{#if onToggleDetails}
|
||||||
aria-label="Details"
|
<button
|
||||||
aria-expanded={detailsOpen}
|
type="button"
|
||||||
onclick={onToggleDetails}
|
class="details-button"
|
||||||
>
|
aria-label="Details"
|
||||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
aria-expanded={detailsOpen}
|
||||||
<circle cx="5" cy="12" r="2" />
|
onclick={onToggleDetails}
|
||||||
<circle cx="12" cy="12" r="2" />
|
>
|
||||||
<circle cx="19" cy="12" r="2" />
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||||
</svg>
|
<circle cx="5" cy="12" r="2" />
|
||||||
</button>
|
<circle cx="12" cy="12" r="2" />
|
||||||
|
<circle cx="19" cy="12" r="2" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -72,30 +79,39 @@
|
|||||||
color: var(--line-strong);
|
color: var(--line-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.worker-context-actions {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.details-button {
|
.details-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 20px;
|
width: var(--space-5);
|
||||||
height: var(--line-height-compact);
|
height: var(--space-5);
|
||||||
margin-left: auto;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-radius: var(--space-1);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-button:hover,
|
.details-button:hover,
|
||||||
.details-button[aria-expanded="true"],
|
.details-button[aria-expanded="true"] {
|
||||||
.details-button:focus-visible {
|
background: var(--bg-subtle);
|
||||||
color: var(--text);
|
color: var(--text-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-button:focus-visible {
|
.details-button:focus-visible {
|
||||||
|
color: var(--text-strong);
|
||||||
outline: 1px solid currentColor;
|
outline: 1px solid currentColor;
|
||||||
outline-offset: -1px;
|
outline-offset: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-button svg {
|
.details-button svg {
|
||||||
|
|||||||
@@ -756,6 +756,12 @@ Deno.test("Model information stays below Composer in one compact monospace row",
|
|||||||
"line-height: var(--line-height-compact)", "padding-inline: var(--space-3)",
|
"line-height: var(--line-height-compact)", "padding-inline: var(--space-3)",
|
||||||
]) assert(component.includes(rule), rule);
|
]) assert(component.includes(rule), rule);
|
||||||
assert(!component.includes("@media"), "Narrow layouts must retain the same single row");
|
assert(!component.includes("@media"), "Narrow layouts must retain the same single row");
|
||||||
|
const modes = page.indexOf('class="console-view-toggle"');
|
||||||
|
assert(modes > status && modes < page.indexOf("</WorkerContextStatus>", status), "Display toggle must be in the model information row");
|
||||||
|
assert(!page.includes("console-header"), "Remove the empty header and its styles");
|
||||||
|
assert(component.indexOf("{@render controls?.()}") < component.indexOf('class="details-button"'), "Display controls must precede Details");
|
||||||
|
assert(/\.worker-context-actions\s*\{[^}]*flex: 0 0 auto/.test(component), "Keep display controls from shrinking on narrow screens");
|
||||||
|
assert(component.includes("margin-left: auto"), "Align footer controls to the right");
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Console spacing and text metrics use existing design tokens", async () => {
|
Deno.test("Console spacing and text metrics use existing design tokens", async () => {
|
||||||
@@ -1571,14 +1577,10 @@ Deno.test("Composer owns TUI-style completion and replaces Compact/Rewind header
|
|||||||
const input = await Deno.readTextFile(
|
const input = await Deno.readTextFile(
|
||||||
new URL("./ComposerInput.svelte", import.meta.url),
|
new URL("./ComposerInput.svelte", import.meta.url),
|
||||||
);
|
);
|
||||||
const header = page.slice(
|
|
||||||
page.indexOf('<section class="console-header'),
|
|
||||||
page.indexOf("{#if rewindTargets.length"),
|
|
||||||
);
|
|
||||||
assert(
|
assert(
|
||||||
!header.includes("Compact") && !header.includes("Rewind") &&
|
!page.includes('class="console-header') &&
|
||||||
!page.includes("requestRewindTargets"),
|
!page.includes("requestRewindTargets"),
|
||||||
"Header must defer Compact/Rewind to commands",
|
"Compact/Rewind must remain commands without a dedicated header",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
input.includes('role="listbox"') && input.includes('role="option"') &&
|
input.includes('role="listbox"') && input.includes('role="option"') &&
|
||||||
@@ -1608,12 +1610,8 @@ Deno.test("mini task summary owns pane toggling instead of the header", async ()
|
|||||||
const component = await Deno.readTextFile(
|
const component = await Deno.readTextFile(
|
||||||
new URL("./ConsoleTasks.svelte", import.meta.url),
|
new URL("./ConsoleTasks.svelte", import.meta.url),
|
||||||
);
|
);
|
||||||
const header = page.slice(
|
|
||||||
page.indexOf('<section class="console-header'),
|
|
||||||
page.indexOf("{#if rewindTargets.length"),
|
|
||||||
);
|
|
||||||
assert(
|
assert(
|
||||||
!header.includes("taskPaneOpen = !taskPaneOpen") &&
|
!page.includes('class="console-header') &&
|
||||||
page.includes("onTogglePane={() => {"),
|
page.includes("onTogglePane={() => {"),
|
||||||
"Only the mini summary should own Tasks toggling",
|
"Only the mini summary should own Tasks toggling",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1663,33 +1663,6 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="console-shell worker-console-shell">
|
<div class="console-shell worker-console-shell">
|
||||||
<section class="console-header card" aria-label="Worker controls">
|
|
||||||
<div class="console-header-actions">
|
|
||||||
<div
|
|
||||||
class="console-view-modes"
|
|
||||||
role="group"
|
|
||||||
aria-label="Console display mode"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class:active={consoleViewMode === "overview"}
|
|
||||||
aria-pressed={consoleViewMode === "overview"}
|
|
||||||
onclick={() => (consoleViewMode = "overview")}
|
|
||||||
>
|
|
||||||
Overview
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class:active={consoleViewMode === "normal"}
|
|
||||||
aria-pressed={consoleViewMode === "normal"}
|
|
||||||
onclick={() => (consoleViewMode = "normal")}
|
|
||||||
>
|
|
||||||
Normal
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{#if rewindTargets.length > 0}
|
{#if rewindTargets.length > 0}
|
||||||
<section class="card rewind-targets" aria-label="Rewind targets">
|
<section class="card rewind-targets" aria-label="Rewind targets">
|
||||||
<h3>Rewind targets</h3>
|
<h3>Rewind targets</h3>
|
||||||
@@ -2101,7 +2074,21 @@
|
|||||||
workerDetailsOpen = !workerDetailsOpen;
|
workerDetailsOpen = !workerDetailsOpen;
|
||||||
if (workerDetailsOpen) taskPaneOpen = false;
|
if (workerDetailsOpen) taskPaneOpen = false;
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
{#snippet controls()}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="console-view-toggle"
|
||||||
|
role="switch"
|
||||||
|
aria-checked={consoleViewMode === "overview"}
|
||||||
|
title="On: Overview · Off: Normal"
|
||||||
|
onclick={() => (consoleViewMode = consoleViewMode === "overview" ? "normal" : "overview")}
|
||||||
|
>
|
||||||
|
<span class="console-view-toggle-track" aria-hidden="true"></span>
|
||||||
|
<span>Overview</span>
|
||||||
|
</button>
|
||||||
|
{/snippet}
|
||||||
|
</WorkerContextStatus>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -2127,14 +2114,6 @@
|
|||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-header {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: var(--space-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-card {
|
.console-card {
|
||||||
display: grid;
|
display: grid;
|
||||||
align-content: start;
|
align-content: start;
|
||||||
@@ -2142,46 +2121,59 @@
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-header-actions {
|
.console-view-toggle {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
gap: var(--space-1);
|
||||||
flex-wrap: wrap;
|
min-height: var(--space-5);
|
||||||
gap: var(--space-2);
|
padding: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.console-view-modes {
|
|
||||||
display: inline-flex;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 0.55rem;
|
|
||||||
background: var(--bg-raised);
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-view-modes button {
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-radius: var(--space-1);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
padding: var(--space-1) var(--space-2);
|
font-family: var(--font-sans);
|
||||||
font: inherit;
|
|
||||||
font-size: var(--font-size-compact);
|
font-size: var(--font-size-compact);
|
||||||
line-height: var(--line-height-compact);
|
line-height: var(--line-height-compact);
|
||||||
font-weight: 700;
|
font-weight: 400;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-view-modes button + button {
|
.console-view-toggle:hover {
|
||||||
border-left: 1px solid var(--line);
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-view-modes button:hover {
|
|
||||||
color: var(--text-strong);
|
color: var(--text-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-view-modes button.active {
|
.console-view-toggle:focus-visible {
|
||||||
background: var(--accent);
|
outline: 1px solid var(--text-strong);
|
||||||
color: var(--bg);
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-view-toggle-track {
|
||||||
|
position: relative;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 24px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--line-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-view-toggle-track::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-view-toggle[aria-checked="true"] .console-view-toggle-track {
|
||||||
|
background: var(--accent-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-view-toggle[aria-checked="true"] .console-view-toggle-track::before {
|
||||||
|
transform: translateX(10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rewind-targets {
|
.rewind-targets {
|
||||||
@@ -2680,14 +2672,5 @@
|
|||||||
grid-template-columns: minmax(0, 1fr);
|
grid-template-columns: minmax(0, 1fr);
|
||||||
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-header {
|
|
||||||
align-items: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-header-actions {
|
|
||||||
align-self: flex-end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
+18
-3
@@ -261,13 +261,28 @@ test.each(["live_protocol", "retained_snapshot"] as const)("overlapping history
|
|||||||
await screen.findByText("Start of conversation");
|
await screen.findByText("Start of conversation");
|
||||||
expect(view.container.querySelectorAll(".activity-summary")).toHaveLength(1);
|
expect(view.container.querySelectorAll(".activity-summary")).toHaveLength(1);
|
||||||
expect(screen.getAllByText("searched 1 time・ran 1 command")).toHaveLength(1);
|
expect(screen.getAllByText("searched 1 time・ran 1 command")).toHaveLength(1);
|
||||||
await fireEvent.click(screen.getByRole("button", { name: "Normal" }));
|
const footer = screen.getByLabelText("Worker model and context");
|
||||||
|
const overview = screen.getByRole("switch", { name: "Overview" });
|
||||||
|
expect(screen.queryByRole("button", { name: "Normal" })).toBeNull();
|
||||||
|
const details = screen.getByRole("button", { name: "Details" });
|
||||||
|
expect(view.container.querySelector(".console-header")).toBeNull();
|
||||||
|
expect(footer.contains(overview)).toBe(true);
|
||||||
|
expect(overview.nextElementSibling).toBe(details);
|
||||||
|
expect(overview.closest("form")).toBeNull();
|
||||||
|
expect(view.container.querySelector(".console-composer")?.nextElementSibling).toBe(footer);
|
||||||
|
expect(overview.getAttribute("aria-checked")).toBe("true");
|
||||||
|
await fireEvent.click(overview);
|
||||||
|
expect(overview.getAttribute("aria-checked")).toBe("false");
|
||||||
|
expect(overview.textContent?.trim()).toBe("Overview");
|
||||||
const transcript = screen.getByRole("article", { name: "main transcript" });
|
const transcript = screen.getByRole("article", { name: "main transcript" });
|
||||||
const ids = [...transcript.querySelectorAll("[data-console-line-id]")].map((line) => line.getAttribute("data-console-line-id"));
|
const ids = [...transcript.querySelectorAll("[data-console-line-id]")].map((line) => line.getAttribute("data-console-line-id"));
|
||||||
expect(ids).toHaveLength(3);
|
expect(ids).toHaveLength(3);
|
||||||
expect(new Set(ids).size).toBe(ids.length);
|
expect(new Set(ids).size).toBe(ids.length);
|
||||||
await fireEvent.click(screen.getByRole("button", { name: "Overview" }));
|
await fireEvent.click(overview);
|
||||||
|
expect(overview.getAttribute("aria-checked")).toBe("true");
|
||||||
|
expect(overview.textContent?.trim()).toBe("Overview");
|
||||||
expect(view.container.querySelectorAll(".activity-summary")).toHaveLength(1);
|
expect(view.container.querySelectorAll(".activity-summary")).toHaveLength(1);
|
||||||
|
expect(multiplexer.sendWorkerMethod).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
@@ -473,7 +488,7 @@ test("mini task summary opens and closes details without a header Tasks button",
|
|||||||
latestListener().onFrame(subscribedFrame(session));
|
latestListener().onFrame(subscribedFrame(session));
|
||||||
const summary = await screen.findByRole("button", { name: /1 task — pending/ });
|
const summary = await screen.findByRole("button", { name: /1 task — pending/ });
|
||||||
expect(screen.queryByRole("complementary", { name: "Worker tasks" })).toBeNull();
|
expect(screen.queryByRole("complementary", { name: "Worker tasks" })).toBeNull();
|
||||||
expect(view.container.querySelector(".console-header")?.textContent).not.toContain("Tasks");
|
expect(view.container.querySelector(".console-header")).toBeNull();
|
||||||
await fireEvent.click(screen.getByRole("button", { name: "Details" }));
|
await fireEvent.click(screen.getByRole("button", { name: "Details" }));
|
||||||
const details = await screen.findByRole("complementary", { name: "Worker detail" });
|
const details = await screen.findByRole("complementary", { name: "Worker detail" });
|
||||||
expect(details.textContent).not.toContain("Capabilities");
|
expect(details.textContent).not.toContain("Capabilities");
|
||||||
|
|||||||
Reference in New Issue
Block a user