Merge commit 'c04442f6ea8ead6b65330bc03dc434dc9278985c'
This commit is contained in:
@@ -90,3 +90,5 @@ Dashboard Phase 1 resume instrumentation adds observation-only client/server tra
|
||||
- Server ring (5000, in-memory): `GET /api/diagnostics/resume-events?limit=&since=&view=` returns `{ events, droppedSinceLastRead }`
|
||||
- Client batching: POST `/api/diagnostics/resume-events` in idle batches (`<=25` per POST).
|
||||
- Disable knob: `window.__fusionDebug.resumeInstrumentation.setEnabled(false)`.
|
||||
|
||||
FN-5415 extends this coverage across remaining board/data visibility hooks: `useNodes`, `useMeshState`, `useProjects`, and `useManagedDockerNodes`. Each now emits `trigger: "visibility"` with `reason: "debounced-refresh"` when refresh is taken and `reason: "debounce-skipped"` (including `detail.timeSinceLastRefreshMs`) when suppressed by debounce. This completes board/data-hook resume-correlation coverage needed for FN-5392 Phase 2 remediation analysis.
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as api from "../../api";
|
||||
|
||||
const { recordResumeEvent } = vi.hoisted(() => ({
|
||||
recordResumeEvent: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
fetchManagedDockerNodes: vi.fn(),
|
||||
fetchManagedDockerNodeContainerStatus: vi.fn(),
|
||||
fetchDockerNodeLogs: vi.fn(),
|
||||
createManagedDockerNode: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/resumeInstrumentation", () => ({
|
||||
recordResumeEvent,
|
||||
}));
|
||||
|
||||
const mockFetchManagedDockerNodes = vi.mocked(api.fetchManagedDockerNodes);
|
||||
|
||||
async function flushPromises(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
describe("useManagedDockerNodes resume instrumentation", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
mockFetchManagedDockerNodes.mockReset();
|
||||
recordResumeEvent.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("emits debounced-refresh then debounce-skipped and refreshes once for visibility events", async () => {
|
||||
mockFetchManagedDockerNodes.mockResolvedValue([]);
|
||||
Object.defineProperty(document, "visibilityState", { value: "visible", configurable: true });
|
||||
|
||||
const { useManagedDockerNodes } = await import("../useManagedDockerNodes");
|
||||
renderHook(() => useManagedDockerNodes());
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
// Reset to isolate visibility-driven refreshes from initial mount fetch.
|
||||
mockFetchManagedDockerNodes.mockClear();
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1100);
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||||
view: "useManagedDockerNodes",
|
||||
trigger: "visibility",
|
||||
reason: "debounced-refresh",
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
||||
view: "useManagedDockerNodes",
|
||||
trigger: "visibility",
|
||||
reason: "debounce-skipped",
|
||||
detail: expect.objectContaining({
|
||||
timeSinceLastRefreshMs: expect.any(Number),
|
||||
}),
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenCalledTimes(2);
|
||||
expect(mockFetchManagedDockerNodes).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as api from "../../api";
|
||||
|
||||
const { recordResumeEvent } = vi.hoisted(() => ({
|
||||
recordResumeEvent: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
fetchMeshState: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/resumeInstrumentation", () => ({
|
||||
recordResumeEvent,
|
||||
}));
|
||||
|
||||
const mockFetchMeshState = vi.mocked(api.fetchMeshState);
|
||||
|
||||
async function flushPromises(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
function meshPayload(nodeId: string) {
|
||||
return {
|
||||
collectedAt: "2026-01-01T00:00:00.000Z",
|
||||
sourceNodeId: "local",
|
||||
nodes: [{ nodeId, nodeName: nodeId, nodeUrl: undefined, nodeType: "local", status: "online", metrics: null, lastSeen: "2026-01-01T00:00:00.000Z", connectedAt: "2026-01-01T00:00:00.000Z", knownPeers: [] }],
|
||||
};
|
||||
}
|
||||
|
||||
describe("useMeshState resume instrumentation", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
mockFetchMeshState.mockReset();
|
||||
recordResumeEvent.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("emits debounced-refresh then debounce-skipped and refreshes once for visibility events", async () => {
|
||||
mockFetchMeshState.mockResolvedValue(meshPayload("local"));
|
||||
Object.defineProperty(document, "visibilityState", { value: "visible", configurable: true });
|
||||
|
||||
const { useMeshState } = await import("../useMeshState");
|
||||
renderHook(() => useMeshState());
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
// Reset to isolate visibility-driven refreshes from initial mount fetch.
|
||||
mockFetchMeshState.mockClear();
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1100);
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||||
view: "useMeshState",
|
||||
trigger: "visibility",
|
||||
reason: "debounced-refresh",
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
||||
view: "useMeshState",
|
||||
trigger: "visibility",
|
||||
reason: "debounce-skipped",
|
||||
detail: expect.objectContaining({
|
||||
timeSinceLastRefreshMs: expect.any(Number),
|
||||
}),
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenCalledTimes(2);
|
||||
expect(mockFetchMeshState).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as api from "../../api";
|
||||
|
||||
const { recordResumeEvent } = vi.hoisted(() => ({
|
||||
recordResumeEvent: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
fetchNodes: vi.fn(),
|
||||
registerNode: vi.fn(),
|
||||
updateNode: vi.fn(),
|
||||
unregisterNode: vi.fn(),
|
||||
checkNodeHealth: vi.fn(),
|
||||
discoverRemoteNodeProjects: vi.fn(),
|
||||
fetchDockerNodeConfig: vi.fn(),
|
||||
updateDockerNodeConfig: vi.fn(),
|
||||
fetchDockerConfigDiff: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../api-node", () => ({
|
||||
persistNodeProjectPathMappings: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/resumeInstrumentation", () => ({
|
||||
recordResumeEvent,
|
||||
}));
|
||||
|
||||
const mockFetchNodes = vi.mocked(api.fetchNodes);
|
||||
|
||||
async function flushPromises(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
describe("useNodes resume instrumentation", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
mockFetchNodes.mockReset();
|
||||
recordResumeEvent.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("emits debounced-refresh then debounce-skipped and refreshes once for visibility events", async () => {
|
||||
mockFetchNodes.mockResolvedValue([]);
|
||||
Object.defineProperty(document, "visibilityState", { value: "visible", configurable: true });
|
||||
|
||||
const { useNodes } = await import("../useNodes");
|
||||
renderHook(() => useNodes());
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
// Reset to isolate visibility-driven refreshes from initial mount fetch.
|
||||
mockFetchNodes.mockClear();
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1100);
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||||
view: "useNodes",
|
||||
trigger: "visibility",
|
||||
reason: "debounced-refresh",
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
||||
view: "useNodes",
|
||||
trigger: "visibility",
|
||||
reason: "debounce-skipped",
|
||||
detail: expect.objectContaining({
|
||||
timeSinceLastRefreshMs: expect.any(Number),
|
||||
}),
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenCalledTimes(2);
|
||||
expect(mockFetchNodes).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as api from "../../api";
|
||||
import * as swrCache from "../../utils/swrCache";
|
||||
|
||||
const { recordResumeEvent } = vi.hoisted(() => ({
|
||||
recordResumeEvent: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
fetchProjectsAcrossNodes: vi.fn(),
|
||||
registerProject: vi.fn(),
|
||||
updateProject: vi.fn(),
|
||||
unregisterProject: vi.fn(),
|
||||
hasNodeMappingsSupport: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../utils/resumeInstrumentation", () => ({
|
||||
recordResumeEvent,
|
||||
}));
|
||||
|
||||
const mockFetchProjectsAcrossNodes = vi.mocked(api.fetchProjectsAcrossNodes);
|
||||
const mockHasNodeMappingsSupport = vi.mocked(api.hasNodeMappingsSupport);
|
||||
const mockReadCache = vi.spyOn(swrCache, "readCache");
|
||||
|
||||
async function flushPromises(): Promise<void> {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
describe("useProjects resume instrumentation", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
mockFetchProjectsAcrossNodes.mockReset();
|
||||
mockHasNodeMappingsSupport.mockReset();
|
||||
mockReadCache.mockReset();
|
||||
recordResumeEvent.mockReset();
|
||||
mockReadCache.mockReturnValue(null);
|
||||
mockHasNodeMappingsSupport.mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("emits debounced-refresh then debounce-skipped and refreshes once for visibility events", async () => {
|
||||
mockFetchProjectsAcrossNodes.mockResolvedValue([]);
|
||||
Object.defineProperty(document, "visibilityState", { value: "visible", configurable: true });
|
||||
|
||||
const { useProjects } = await import("../useProjects");
|
||||
renderHook(() => useProjects());
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
// Reset to isolate visibility-driven refreshes from initial mount fetch.
|
||||
mockFetchProjectsAcrossNodes.mockClear();
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1100);
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
document.dispatchEvent(new Event("visibilitychange"));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
||||
view: "useProjects",
|
||||
trigger: "visibility",
|
||||
reason: "debounced-refresh",
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
||||
view: "useProjects",
|
||||
trigger: "visibility",
|
||||
reason: "debounce-skipped",
|
||||
detail: expect.objectContaining({
|
||||
timeSinceLastRefreshMs: expect.any(Number),
|
||||
}),
|
||||
}));
|
||||
expect(recordResumeEvent).toHaveBeenCalledTimes(2);
|
||||
expect(mockFetchProjectsAcrossNodes).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
fetchManagedDockerNodeContainerStatus,
|
||||
fetchManagedDockerNodes,
|
||||
} from "../api";
|
||||
import { recordResumeEvent } from "../utils/resumeInstrumentation";
|
||||
|
||||
export interface UseManagedDockerNodesResult {
|
||||
dockerNodes: ManagedDockerNodeInfo[];
|
||||
@@ -68,11 +69,27 @@ export function useManagedDockerNodes(): UseManagedDockerNodesResult {
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (now - lastVisibilityRefreshRef.current < VISIBILITY_REFRESH_DEBOUNCE_MS) {
|
||||
const timeSinceLastRefresh = now - lastVisibilityRefreshRef.current;
|
||||
if (timeSinceLastRefresh < VISIBILITY_REFRESH_DEBOUNCE_MS) {
|
||||
recordResumeEvent({
|
||||
view: "useManagedDockerNodes",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounce-skipped",
|
||||
detail: { timeSinceLastRefreshMs: timeSinceLastRefresh },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
lastVisibilityRefreshRef.current = now;
|
||||
recordResumeEvent({
|
||||
view: "useManagedDockerNodes",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounced-refresh",
|
||||
});
|
||||
void refresh();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import type { NodeMeshState } from "@fusion/core";
|
||||
import { fetchMeshState } from "../api";
|
||||
import { recordResumeEvent } from "../utils/resumeInstrumentation";
|
||||
|
||||
const POLL_INTERVAL_MS = 10000;
|
||||
const VISIBILITY_REFRESH_DEBOUNCE_MS = 1000;
|
||||
@@ -56,8 +57,26 @@ export function useMeshState(): UseMeshStateResult {
|
||||
const handleVisibilityChange = () => {
|
||||
if (document.visibilityState !== "visible") return;
|
||||
const now = Date.now();
|
||||
if (now - lastVisibilityRefreshRef.current < VISIBILITY_REFRESH_DEBOUNCE_MS) return;
|
||||
const timeSinceLastRefresh = now - lastVisibilityRefreshRef.current;
|
||||
if (timeSinceLastRefresh < VISIBILITY_REFRESH_DEBOUNCE_MS) {
|
||||
recordResumeEvent({
|
||||
view: "useMeshState",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounce-skipped",
|
||||
detail: { timeSinceLastRefreshMs: timeSinceLastRefresh },
|
||||
});
|
||||
return;
|
||||
}
|
||||
lastVisibilityRefreshRef.current = now;
|
||||
recordResumeEvent({
|
||||
view: "useMeshState",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounced-refresh",
|
||||
});
|
||||
void refresh();
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
discoverRemoteNodeProjects,
|
||||
} from "../api";
|
||||
import { persistNodeProjectPathMappings } from "../api-node";
|
||||
import { recordResumeEvent } from "../utils/resumeInstrumentation";
|
||||
|
||||
export interface UseNodesResult {
|
||||
nodes: NodeInfo[];
|
||||
@@ -86,10 +87,25 @@ export function useNodes(): UseNodesResult {
|
||||
const now = Date.now();
|
||||
const timeSinceLastRefresh = now - lastVisibilityRefreshRef.current;
|
||||
if (timeSinceLastRefresh < VISIBILITY_REFRESH_DEBOUNCE_MS) {
|
||||
recordResumeEvent({
|
||||
view: "useNodes",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounce-skipped",
|
||||
detail: { timeSinceLastRefreshMs: timeSinceLastRefresh },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
lastVisibilityRefreshRef.current = now;
|
||||
recordResumeEvent({
|
||||
view: "useNodes",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounced-refresh",
|
||||
});
|
||||
void refresh();
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type ProjectNodeAvailability,
|
||||
} from "../api";
|
||||
import { SWR_CACHE_KEYS, SWR_DEFAULT_MAX_AGE_MS, clearCache, readCache, writeCache } from "../utils/swrCache";
|
||||
import { recordResumeEvent } from "../utils/resumeInstrumentation";
|
||||
|
||||
export interface UseProjectsResult {
|
||||
/** List of all registered projects (local + remote) */
|
||||
@@ -147,10 +148,25 @@ export function useProjects(): UseProjectsResult {
|
||||
const now = Date.now();
|
||||
const timeSinceLastRefresh = now - lastVisibilityRefreshRef.current;
|
||||
if (timeSinceLastRefresh < VISIBILITY_REFRESH_DEBOUNCE_MS) {
|
||||
recordResumeEvent({
|
||||
view: "useProjects",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounce-skipped",
|
||||
detail: { timeSinceLastRefreshMs: timeSinceLastRefresh },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
lastVisibilityRefreshRef.current = now;
|
||||
recordResumeEvent({
|
||||
view: "useProjects",
|
||||
trigger: "visibility",
|
||||
projectId: undefined,
|
||||
replayAttempted: false,
|
||||
reason: "debounced-refresh",
|
||||
});
|
||||
void refresh();
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const qualityAppTests = [
|
||||
"app/components/__tests__/{ActiveAgentsPanel,ActivityLogModal,AgentMentionPopup,AgentMetricsBar,AgentOnboardingModal,AgentReflectionsTab,AgentTokenStatsPanel,App,AuthTokenRecoveryDialog,Board,board-mobile,board-mobile-view-switch,ChatView,ChatView.autosize,ChatView.chat-input-autosize,ChatView.default-model-icon,ChatView.draft,ChatView.hash-mention,ChatView.rooms,ChatView.scroll-to-top,ChatView.swipe-back,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DevServerView.mobile,DirectoryPicker,DuplicateWarningModal,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,GitHubBadge,InlineCreateCard,LoginInstructions,MemoryView,MergeAdvanceNotice,MessageComposer,MessageComposer.autosize,MobileNavBar,NewTaskModal,NewTaskModal.shared-cache,NodeCard,NodeHealthDot,NodeStatusIndicator,PlanningModeModal.autosize,PrChecksList,PrCreateModal,PrCreateModal.layout,ProjectCard,ProjectSelector,ProviderIcon,PrPanel,PrPanel.merge,PrPanel.reviews,QuickChatFAB,QuickChatFAB.shared-cache,ReliabilityView,ResearchView,SecretsView,SecretsView.mobile,SettingsModal,SettingsModal.worktrunk,StashRecoveryView,TaskCard,TaskCard.badge-height,TaskCard.badge-wrap,TaskCard.footer-wrap,TaskChangesTab,TaskComments,TaskDetailModal,TaskDetailModal.create-pr-e2e,TaskDetailModal.create-pr-integration,TaskDetailModal.github-tracking-header,TaskDetailModal.github-tracking-stale,TaskDetailModal.rebind-banner,TaskDocumentsTab,TaskForm,TaskIdIntegrityBanner,TrackingRepoSelect,WorkflowResultsTab,WorktrunkInstallApprovalDetails}.test.tsx",
|
||||
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
||||
"app/context/**/*.test.tsx",
|
||||
"app/hooks/__tests__/{useAgents,useAgentLogs,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodeSettingsSync,useProjects,useQuickChat,useTasks,useTasks.resume-instrumentation,useChatRooms.resume-instrumentation,useTerminalSessions,useTheme,useToast,useUsageData,useViewState}.test.{ts,tsx}",
|
||||
"app/hooks/__tests__/{useAgents,useAgentLogs,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodes.resume-instrumentation,useNodeSettingsSync,useProjects,useProjects.resume-instrumentation,useMeshState.resume-instrumentation,useManagedDockerNodes.resume-instrumentation,useQuickChat,useTasks,useTasks.resume-instrumentation,useChatRooms.resume-instrumentation,useTerminalSessions,useTheme,useToast,useUsageData,useViewState}.test.{ts,tsx}",
|
||||
"app/utils/**/*.test.{ts,tsx}",
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user