mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
fix(ui): publish long-thread metrics after StrictMode cleanup
Co-Authored-By: Paperclip <noreply@paperclip.ing>
(cherry picked from commit 3807ee526d)
This commit is contained in:
1 parent
8ce4371cd2
commit
9375ac95cc
2 files changed
+77
-11
No files matched your search
@@ -0,0 +1,58 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { StrictMode } from "react";
|
||||
import { flushSync } from "react-dom";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { afterEach, beforeEach, expect, it, vi } from "vitest";
|
||||
import { IssueChatLongThreadPerf } from "./IssueChatLongThreadPerf";
|
||||
|
||||
vi.mock("../components/IssueChatThread", () => ({ IssueChatThread: () => null }));
|
||||
|
||||
const mounted: Array<{ root: Root; container: HTMLDivElement }> = [];
|
||||
|
||||
beforeEach(() => vi.useFakeTimers());
|
||||
afterEach(() => {
|
||||
for (const { root, container } of mounted.splice(0)) {
|
||||
flushSync(() => root.unmount());
|
||||
container.remove();
|
||||
}
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
function mount(strict: boolean) {
|
||||
const container = document.createElement("div");
|
||||
document.body.append(container);
|
||||
const root = createRoot(container);
|
||||
const view = { root, container };
|
||||
mounted.push(view);
|
||||
flushSync(() => root.render(strict ? <StrictMode><IssueChatLongThreadPerf /></StrictMode> : <IssueChatLongThreadPerf />));
|
||||
return view;
|
||||
}
|
||||
|
||||
function metric(container: HTMLElement, name: string) {
|
||||
return container.querySelector(`[data-testid="perf-${name}"]`)!.textContent!;
|
||||
}
|
||||
|
||||
it.each([false, true])("publishes finite actual fixture metrics with StrictMode=%s", strict => {
|
||||
const { container } = mount(strict);
|
||||
flushSync(() => vi.runOnlyPendingTimers());
|
||||
for (const name of ["mount-duration", "latest-duration", "max-duration", "total-duration"])
|
||||
expect(metric(container, name)).toMatch(/^\d+(?:\.\d+)? ms$/);
|
||||
expect(Number(metric(container, "commit-count"))).toBeGreaterThanOrEqual(1);
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
});
|
||||
|
||||
it("cancels unpublished metrics on unmount and allows a fresh StrictMode mount", () => {
|
||||
const first = mount(true);
|
||||
expect(vi.getTimerCount()).toBe(1);
|
||||
flushSync(() => first.root.unmount());
|
||||
first.container.remove();
|
||||
mounted.splice(mounted.indexOf(first), 1);
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
flushSync(() => vi.runOnlyPendingTimers());
|
||||
expect(first.container.textContent).toBe("");
|
||||
const second = mount(true);
|
||||
flushSync(() => vi.runOnlyPendingTimers());
|
||||
expect(metric(second.container, "mount-duration")).toMatch(/^\d+(?:\.\d+)? ms$/);
|
||||
expect(vi.getTimerCount()).toBe(0);
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Profiler, useEffect, useLayoutEffect, useMemo, useRef, useState, type ProfilerOnRenderCallback } from "react";
|
||||
import { Profiler, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type ProfilerOnRenderCallback } from "react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { IssueChatThread } from "../components/IssueChatThread";
|
||||
@@ -63,12 +63,25 @@ export function IssueChatLongThreadPerf() {
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (publishTimerRef.current !== null) {
|
||||
window.clearTimeout(publishTimerRef.current);
|
||||
}
|
||||
const schedulePublication = useCallback(() => {
|
||||
if (publishedRef.current || publishTimerRef.current !== null) return;
|
||||
publishTimerRef.current = window.setTimeout(() => {
|
||||
publishTimerRef.current = null;
|
||||
publishedRef.current = true;
|
||||
setMetrics(metricsRef.current);
|
||||
}, 0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (metricsRef.current.commitCount > 0) schedulePublication();
|
||||
return () => {
|
||||
if (publishTimerRef.current !== null) {
|
||||
window.clearTimeout(publishTimerRef.current);
|
||||
publishTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [schedulePublication]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (publishedRef.current || metricsRef.current.commitCount > 0) return;
|
||||
const mountDuration = performance.now() - renderStartedAtRef.current;
|
||||
@@ -96,12 +109,7 @@ export function IssueChatLongThreadPerf() {
|
||||
totalActualDuration: current.totalActualDuration + actualDuration,
|
||||
};
|
||||
|
||||
if (publishedRef.current || publishTimerRef.current !== null) return;
|
||||
publishTimerRef.current = window.setTimeout(() => {
|
||||
publishTimerRef.current = null;
|
||||
publishedRef.current = true;
|
||||
setMetrics(metricsRef.current);
|
||||
}, 0);
|
||||
schedulePublication();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in new issue
Block a user