feat(FN-2619): merge fusion/fn-2619
This commit is contained in:
@@ -268,10 +268,8 @@ export function AgentLogViewer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
|
||||||
className={`agent-log-viewer agent-log-viewer--streaming${isFullscreen ? " agent-log-viewer--fullscreen" : ""}`}
|
className={`agent-log-viewer agent-log-viewer--streaming${isFullscreen ? " agent-log-viewer--fullscreen" : ""}`}
|
||||||
data-testid="agent-log-viewer"
|
data-testid="agent-log-viewer"
|
||||||
onScroll={handleScroll}
|
|
||||||
>
|
>
|
||||||
{/* Model info header */}
|
{/* Model info header */}
|
||||||
<div className="agent-log-model-header" data-testid="agent-log-model-header">
|
<div className="agent-log-model-header" data-testid="agent-log-model-header">
|
||||||
@@ -357,66 +355,106 @@ export function AgentLogViewer({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination summary */}
|
<div
|
||||||
{totalCount !== null && (
|
ref={containerRef}
|
||||||
<div className="agent-log-summary" data-testid="agent-log-summary">
|
className="agent-log-viewer-scroll"
|
||||||
Showing {entries.length} of {totalCount} entries
|
onScroll={handleScroll}
|
||||||
</div>
|
>
|
||||||
)}
|
{/* Pagination summary */}
|
||||||
|
{totalCount !== null && (
|
||||||
|
<div className="agent-log-summary" data-testid="agent-log-summary">
|
||||||
|
Showing {entries.length} of {totalCount} entries
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{hasMore && onLoadMore && (
|
{hasMore && onLoadMore && (
|
||||||
<div className="agent-log-load-more" data-testid="agent-log-load-more">
|
<div className="agent-log-load-more" data-testid="agent-log-load-more">
|
||||||
<button
|
<button
|
||||||
className="agent-log-mode-toggle"
|
className="agent-log-mode-toggle"
|
||||||
onClick={onLoadMore}
|
onClick={onLoadMore}
|
||||||
disabled={loadingMore}
|
disabled={loadingMore}
|
||||||
data-testid="agent-log-load-more-button"
|
data-testid="agent-log-load-more-button"
|
||||||
>
|
>
|
||||||
{loadingMore ? (
|
{loadingMore ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 size={14} className="animate-spin" />
|
<Loader2 size={14} className="animate-spin" />
|
||||||
Loading…
|
Loading…
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
"Load More"
|
"Load More"
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{entries.map((entry, i) => {
|
{entries.map((entry, i) => {
|
||||||
const rowKey = chronologicalEntryKeys[i] ?? `${getEntrySignature(entry)}|fallback`;
|
const rowKey = chronologicalEntryKeys[i] ?? `${getEntrySignature(entry)}|fallback`;
|
||||||
const prev = entries[i - 1];
|
const prev = entries[i - 1];
|
||||||
const isBlockLevel = entry.type === "tool" || entry.type === "tool_result" || entry.type === "tool_error";
|
const isBlockLevel = entry.type === "tool" || entry.type === "tool_result" || entry.type === "tool_error";
|
||||||
const showBadge = entry.agent
|
const showBadge = entry.agent
|
||||||
? isBlockLevel || !prev || prev.agent !== entry.agent || prev.type !== entry.type
|
? isBlockLevel || !prev || prev.agent !== entry.agent || prev.type !== entry.type
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
const timestampSpan = showBadge ? (
|
const timestampSpan = showBadge ? (
|
||||||
<span className="agent-log-timestamp" data-testid="agent-log-timestamp">
|
<span className="agent-log-timestamp" data-testid="agent-log-timestamp">
|
||||||
{formatTimestamp(entry.timestamp)}
|
{formatTimestamp(entry.timestamp)}
|
||||||
</span>
|
</span>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const agentBadge = showBadge ? (
|
const agentBadge = showBadge ? (
|
||||||
<span className="agent-log-badge-row">
|
<span className="agent-log-badge-row">
|
||||||
<span className="agent-log-agent-badge">[{entry.agent}]</span>
|
<span className="agent-log-agent-badge">[{entry.agent}]</span>
|
||||||
{timestampSpan}
|
{timestampSpan}
|
||||||
</span>
|
</span>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
if (entry.type === "tool") {
|
if (entry.type === "tool") {
|
||||||
|
return (
|
||||||
|
<div key={rowKey} className="agent-log-tool">
|
||||||
|
{agentBadge}⚡ {entry.text}
|
||||||
|
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === "thinking") {
|
||||||
|
return (
|
||||||
|
<div key={rowKey} className="agent-log-thinking">
|
||||||
|
{agentBadge}
|
||||||
|
{renderMarkdown ? (
|
||||||
|
<div className="markdown-body">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||||
|
{entry.text}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
entry.text
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === "tool_result") {
|
||||||
|
return (
|
||||||
|
<div key={rowKey} className="agent-log-tool-result">
|
||||||
|
{agentBadge}✓ {entry.text}
|
||||||
|
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === "tool_error") {
|
||||||
|
return (
|
||||||
|
<div key={rowKey} className="agent-log-tool-error">
|
||||||
|
{agentBadge}✗ {entry.text}
|
||||||
|
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: text entries
|
||||||
return (
|
return (
|
||||||
<div key={rowKey} className="agent-log-tool">
|
<div key={rowKey} className="agent-log-text">
|
||||||
{agentBadge}⚡ {entry.text}
|
|
||||||
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.type === "thinking") {
|
|
||||||
return (
|
|
||||||
<div key={rowKey} className="agent-log-thinking">
|
|
||||||
{agentBadge}
|
{agentBadge}
|
||||||
{renderMarkdown ? (
|
{renderMarkdown ? (
|
||||||
<div className="markdown-body">
|
<div className="markdown-body">
|
||||||
@@ -429,54 +467,20 @@ export function AgentLogViewer({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
})}
|
||||||
|
|
||||||
if (entry.type === "tool_result") {
|
{!isFollowing && (
|
||||||
return (
|
<button
|
||||||
<div key={rowKey} className="agent-log-tool-result">
|
type="button"
|
||||||
{agentBadge}✓ {entry.text}
|
className="agent-log-return-to-live"
|
||||||
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
onClick={scrollToLive}
|
||||||
</div>
|
data-testid="agent-log-return-to-live"
|
||||||
);
|
>
|
||||||
}
|
<ChevronDown size={12} />
|
||||||
|
<span>Live</span>
|
||||||
if (entry.type === "tool_error") {
|
</button>
|
||||||
return (
|
)}
|
||||||
<div key={rowKey} className="agent-log-tool-error">
|
</div>
|
||||||
{agentBadge}✗ {entry.text}
|
|
||||||
{entry.detail && <span className="agent-log-tool-detail">— {entry.detail}</span>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default: text entries
|
|
||||||
return (
|
|
||||||
<div key={rowKey} className="agent-log-text">
|
|
||||||
{agentBadge}
|
|
||||||
{renderMarkdown ? (
|
|
||||||
<div className="markdown-body">
|
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
|
||||||
{entry.text}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
entry.text
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{!isFollowing && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="agent-log-return-to-live"
|
|
||||||
onClick={scrollToLive}
|
|
||||||
data-testid="agent-log-return-to-live"
|
|
||||||
>
|
|
||||||
<ChevronDown size={12} />
|
|
||||||
<span>Live</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,6 +232,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.detail-section--agent-log .agent-log-viewer {
|
.detail-section--agent-log .agent-log-viewer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-log-viewer-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -241,8 +251,6 @@
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
padding: var(--space-md);
|
padding: var(--space-md);
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
@@ -474,20 +482,23 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: var(--space-lg);
|
padding: var(--space-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-log-viewer--fullscreen .agent-log-model-header {
|
.agent-log-viewer--fullscreen .agent-log-model-header {
|
||||||
position: sticky;
|
margin-bottom: 0;
|
||||||
top: 0;
|
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
z-index: 1;
|
border: 1px solid var(--border);
|
||||||
padding-bottom: var(--space-md);
|
}
|
||||||
margin-bottom: var(--space-md);
|
|
||||||
border-bottom: 1px solid var(--border);
|
.agent-log-viewer--fullscreen .agent-log-viewer-scroll {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-spec-edit-trigger {
|
.detail-spec-edit-trigger {
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ function makeEntry(overrides: Partial<AgentLogEntry> = {}): AgentLogEntry {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getScrollContainer(container: HTMLElement): HTMLDivElement {
|
||||||
|
return container.querySelector(".agent-log-viewer-scroll") as HTMLDivElement;
|
||||||
|
}
|
||||||
|
|
||||||
describe("AgentLogViewer", () => {
|
describe("AgentLogViewer", () => {
|
||||||
it("shows loading message when loading with no entries", () => {
|
it("shows loading message when loading with no entries", () => {
|
||||||
render(<AgentLogViewer entries={[]} loading={true} />);
|
render(<AgentLogViewer entries={[]} loading={true} />);
|
||||||
@@ -736,21 +740,21 @@ describe("AgentLogViewer", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("horizontal overflow prevention", () => {
|
describe("horizontal overflow prevention", () => {
|
||||||
it("uses the viewer class for overflow-x handling", () => {
|
it("uses the scroll container class for overflow-x handling", () => {
|
||||||
const longString = "A".repeat(300);
|
const longString = "A".repeat(300);
|
||||||
const entries = [makeEntry({ text: longString })];
|
const entries = [makeEntry({ text: longString })];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLElement;
|
const scrollContainer = getScrollContainer(container);
|
||||||
expect(viewer.classList.contains("agent-log-viewer")).toBe(true);
|
expect(scrollContainer.classList.contains("agent-log-viewer-scroll")).toBe(true);
|
||||||
expect(viewer.style.overflowX).toBe("");
|
expect(scrollContainer.style.overflowX).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the viewer class for overflow-wrap handling", () => {
|
it("uses the scroll container class for overflow-wrap handling", () => {
|
||||||
const entries = [makeEntry({ text: "x".repeat(250) })];
|
const entries = [makeEntry({ text: "x".repeat(250) })];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLElement;
|
const scrollContainer = getScrollContainer(container);
|
||||||
expect(viewer.classList.contains("agent-log-viewer")).toBe(true);
|
expect(scrollContainer.classList.contains("agent-log-viewer-scroll")).toBe(true);
|
||||||
expect(viewer.style.overflowWrap).toBe("");
|
expect(scrollContainer.style.overflowWrap).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders pre elements with overflow-x auto for internal scrolling", () => {
|
it("renders pre elements with overflow-x auto for internal scrolling", () => {
|
||||||
@@ -783,13 +787,13 @@ describe("AgentLogViewer", () => {
|
|||||||
expect(viewer.style.maxHeight).toBe("");
|
expect(viewer.style.maxHeight).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses class-based overflow-y scrolling", () => {
|
it("uses class-based overflow-y scrolling on the entries container", () => {
|
||||||
const entries = [makeEntry()];
|
const entries = [makeEntry()];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLElement;
|
const scrollContainer = getScrollContainer(container);
|
||||||
// Scrolling behavior is now defined in CSS.
|
// Scrolling behavior is now defined in CSS.
|
||||||
expect(viewer.classList.contains("agent-log-viewer")).toBe(true);
|
expect(scrollContainer.classList.contains("agent-log-viewer-scroll")).toBe(true);
|
||||||
expect(viewer.style.overflowY).toBe("");
|
expect(scrollContainer.style.overflowY).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses agent-log-viewer--streaming class when entries are present", () => {
|
it("uses agent-log-viewer--streaming class when entries are present", () => {
|
||||||
@@ -808,6 +812,67 @@ describe("AgentLogViewer", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("sticky header layout", () => {
|
||||||
|
it("renders the model header as a sibling of the scroll container", () => {
|
||||||
|
const entries = [makeEntry()];
|
||||||
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
|
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLElement;
|
||||||
|
const header = screen.getByTestId("agent-log-model-header");
|
||||||
|
const scrollContainer = getScrollContainer(container);
|
||||||
|
|
||||||
|
expect(header.parentElement).toBe(viewer);
|
||||||
|
expect(scrollContainer.parentElement).toBe(viewer);
|
||||||
|
expect(scrollContainer.contains(header)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders log entry rows inside the scroll container", () => {
|
||||||
|
const entries = [
|
||||||
|
makeEntry({ type: "text", text: "hello" }),
|
||||||
|
makeEntry({ type: "tool", text: "Bash" }),
|
||||||
|
];
|
||||||
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
|
const scrollContainer = getScrollContainer(container);
|
||||||
|
|
||||||
|
expect(scrollContainer.querySelector(".agent-log-text")).toBeTruthy();
|
||||||
|
expect(scrollContainer.querySelector(".agent-log-tool")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders pagination summary and load-more controls inside the scroll container", () => {
|
||||||
|
const entries = [makeEntry({ text: "hello" })];
|
||||||
|
const { container } = render(
|
||||||
|
<AgentLogViewer
|
||||||
|
entries={entries}
|
||||||
|
loading={false}
|
||||||
|
totalCount={42}
|
||||||
|
hasMore={true}
|
||||||
|
onLoadMore={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
const scrollContainer = getScrollContainer(container);
|
||||||
|
|
||||||
|
expect(scrollContainer.querySelector("[data-testid='agent-log-summary']")).toBeTruthy();
|
||||||
|
expect(scrollContainer.querySelector("[data-testid='agent-log-load-more']")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the return-to-live button inside the scroll container", () => {
|
||||||
|
const entries = [
|
||||||
|
makeEntry({ text: "first", timestamp: "2026-01-01T00:00:00Z" }),
|
||||||
|
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
||||||
|
];
|
||||||
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
|
const scrollContainer = getScrollContainer(container);
|
||||||
|
|
||||||
|
Object.defineProperty(scrollContainer, "scrollHeight", { configurable: true, value: 1000 });
|
||||||
|
Object.defineProperty(scrollContainer, "clientHeight", { configurable: true, value: 200 });
|
||||||
|
|
||||||
|
scrollContainer.scrollTop = 300;
|
||||||
|
fireEvent.scroll(scrollContainer);
|
||||||
|
|
||||||
|
const returnToLive = screen.getByTestId("agent-log-return-to-live");
|
||||||
|
expect(returnToLive.parentElement).toBe(scrollContainer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("auto-scroll behavior", () => {
|
describe("auto-scroll behavior", () => {
|
||||||
it("scrolls to bottom when streaming updates arrive and user is near the bottom", () => {
|
it("scrolls to bottom when streaming updates arrive and user is near the bottom", () => {
|
||||||
const initialEntries = [
|
const initialEntries = [
|
||||||
@@ -819,7 +884,7 @@ describe("AgentLogViewer", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
let scrollHeight = 600;
|
let scrollHeight = 600;
|
||||||
Object.defineProperty(viewer, "scrollHeight", {
|
Object.defineProperty(viewer, "scrollHeight", {
|
||||||
@@ -846,7 +911,7 @@ describe("AgentLogViewer", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
let scrollHeight = 1000;
|
let scrollHeight = 1000;
|
||||||
Object.defineProperty(viewer, "scrollHeight", {
|
Object.defineProperty(viewer, "scrollHeight", {
|
||||||
@@ -873,7 +938,7 @@ describe("AgentLogViewer", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
const { rerender, container } = render(<AgentLogViewer entries={initialEntries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
let scrollHeight = 900;
|
let scrollHeight = 900;
|
||||||
Object.defineProperty(viewer, "scrollHeight", {
|
Object.defineProperty(viewer, "scrollHeight", {
|
||||||
@@ -897,7 +962,7 @@ describe("AgentLogViewer", () => {
|
|||||||
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
||||||
];
|
];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
||||||
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
||||||
@@ -914,7 +979,7 @@ describe("AgentLogViewer", () => {
|
|||||||
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
||||||
];
|
];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
||||||
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
||||||
@@ -931,7 +996,7 @@ describe("AgentLogViewer", () => {
|
|||||||
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
makeEntry({ text: "second", timestamp: "2026-01-01T00:00:01Z" }),
|
||||||
];
|
];
|
||||||
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
const { container } = render(<AgentLogViewer entries={entries} loading={false} />);
|
||||||
const viewer = container.querySelector("[data-testid='agent-log-viewer']") as HTMLDivElement;
|
const viewer = getScrollContainer(container);
|
||||||
|
|
||||||
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
Object.defineProperty(viewer, "scrollHeight", { configurable: true, value: 1000 });
|
||||||
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
Object.defineProperty(viewer, "clientHeight", { configurable: true, value: 200 });
|
||||||
|
|||||||
Reference in New Issue
Block a user