feat(FN-4801): merge fusion/fn-4801
This commit is contained in:
5
.changeset/FN-4801-top-progress-revalidation.md
Normal file
5
.changeset/FN-4801-top-progress-revalidation.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Dashboard top progress bar now also reflects task list revalidation, not just project loading.
|
||||||
@@ -382,7 +382,7 @@ function AppInner() {
|
|||||||
// Tasks hook with project context and search query
|
// Tasks hook with project context and search query
|
||||||
// SSE is only enabled for board/list views to free connection slots for mission detail fetches
|
// SSE is only enabled for board/list views to free connection slots for mission detail fetches
|
||||||
const taskSseEnabled = taskView === "board" || taskView === "list";
|
const taskSseEnabled = taskView === "board" || taskView === "list";
|
||||||
const { tasks, createTask, moveTask, pauseTask, unpauseTask, deleteTask, mergeTask, retryTask, resetTask, updateTask, duplicateTask, archiveTask, unarchiveTask, archiveAllDone, loadArchivedTasks, refreshTasks, ingestCreatedTasks, lastFetchTimeMs } = useTasks(
|
const { tasks, isStale, createTask, moveTask, pauseTask, unpauseTask, deleteTask, mergeTask, retryTask, resetTask, updateTask, duplicateTask, archiveTask, unarchiveTask, archiveAllDone, loadArchivedTasks, refreshTasks, ingestCreatedTasks, lastFetchTimeMs } = useTasks(
|
||||||
{
|
{
|
||||||
...(currentProject ? { projectId: currentProject.id } : {}),
|
...(currentProject ? { projectId: currentProject.id } : {}),
|
||||||
searchQuery: searchQuery || undefined,
|
searchQuery: searchQuery || undefined,
|
||||||
@@ -1643,6 +1643,10 @@ function AppInner() {
|
|||||||
isOnboardingCompleted() &&
|
isOnboardingCompleted() &&
|
||||||
!isPostOnboardingDismissed();
|
!isPostOnboardingDismissed();
|
||||||
|
|
||||||
|
// Top progress bar reflects any in-flight revalidation: projects, current-project, or tasks.
|
||||||
|
// Add new sources here, not inside TopProgressBar.
|
||||||
|
const isRevalidating = projectsLoading || currentProjectLoading || isStale;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationHistoryProvider value={{ pushNav, replaceCurrent }}>
|
<NavigationHistoryProvider value={{ pushNav, replaceCurrent }}>
|
||||||
<FileBrowserProvider openFile={openFileInBrowser}>
|
<FileBrowserProvider openFile={openFileInBrowser}>
|
||||||
@@ -1654,7 +1658,7 @@ function AppInner() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<TopProgressBar visible={projectsLoading || currentProjectLoading} />
|
<TopProgressBar visible={isRevalidating} />
|
||||||
<Header
|
<Header
|
||||||
shellHost={shellHost.host}
|
shellHost={shellHost.host}
|
||||||
onOpenSettings={openSettingsWithNav}
|
onOpenSettings={openSettingsWithNav}
|
||||||
|
|||||||
@@ -603,6 +603,7 @@ beforeEach(() => {
|
|||||||
mockUseTasks.mockReset();
|
mockUseTasks.mockReset();
|
||||||
mockUseTasks.mockImplementation(() => ({
|
mockUseTasks.mockImplementation(() => ({
|
||||||
tasks: [],
|
tasks: [],
|
||||||
|
isStale: false,
|
||||||
createTask: mockCreateTask,
|
createTask: mockCreateTask,
|
||||||
moveTask: vi.fn(),
|
moveTask: vi.fn(),
|
||||||
pauseTask: vi.fn(),
|
pauseTask: vi.fn(),
|
||||||
@@ -706,6 +707,84 @@ describe("FN-4250 FileBrowserProvider coverage", () => {
|
|||||||
expect(screen.queryByTestId("fb-probe-loader")).not.toBeInTheDocument();
|
expect(screen.queryByTestId("fb-probe-loader")).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("FN-4801: keeps top progress bar visible while tasks are stale", () => {
|
||||||
|
mockProjectsState.loading = false;
|
||||||
|
mockProjectsState.projects = [
|
||||||
|
{ id: DEFAULT_PROJECT_ID, name: "Test Project", path: "/test", status: "active", isolationMode: "in-process", createdAt: "", updatedAt: "" },
|
||||||
|
];
|
||||||
|
mockCurrentProjectState.loading = false;
|
||||||
|
mockUseTasks.mockImplementation(() => ({
|
||||||
|
tasks: [],
|
||||||
|
isStale: true,
|
||||||
|
createTask: mockCreateTask,
|
||||||
|
moveTask: vi.fn(),
|
||||||
|
pauseTask: vi.fn(),
|
||||||
|
unpauseTask: vi.fn(),
|
||||||
|
deleteTask: vi.fn(),
|
||||||
|
mergeTask: vi.fn(),
|
||||||
|
retryTask: vi.fn(),
|
||||||
|
resetTask: vi.fn(),
|
||||||
|
updateTask: vi.fn(),
|
||||||
|
duplicateTask: vi.fn(),
|
||||||
|
archiveTask: vi.fn(),
|
||||||
|
unarchiveTask: vi.fn(),
|
||||||
|
archiveAllDone: vi.fn(),
|
||||||
|
loadArchivedTasks: vi.fn(),
|
||||||
|
refreshTasks: vi.fn(),
|
||||||
|
ingestCreatedTasks: vi.fn(),
|
||||||
|
lastFetchTimeMs: Date.now(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
const progressBar = screen.getByRole("progressbar", { name: "Loading" });
|
||||||
|
expect(progressBar).toHaveAttribute("aria-busy", "true");
|
||||||
|
expect(progressBar).toHaveAttribute("data-visible", "true");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("FN-4801: hides top progress bar on next render when tasks are fresh", () => {
|
||||||
|
mockProjectsState.loading = false;
|
||||||
|
mockProjectsState.projects = [
|
||||||
|
{ id: DEFAULT_PROJECT_ID, name: "Test Project", path: "/test", status: "active", isolationMode: "in-process", createdAt: "", updatedAt: "" },
|
||||||
|
];
|
||||||
|
mockCurrentProjectState.loading = false;
|
||||||
|
|
||||||
|
const taskHookState = { isStale: true };
|
||||||
|
mockUseTasks.mockImplementation(() => ({
|
||||||
|
tasks: [],
|
||||||
|
isStale: taskHookState.isStale,
|
||||||
|
createTask: mockCreateTask,
|
||||||
|
moveTask: vi.fn(),
|
||||||
|
pauseTask: vi.fn(),
|
||||||
|
unpauseTask: vi.fn(),
|
||||||
|
deleteTask: vi.fn(),
|
||||||
|
mergeTask: vi.fn(),
|
||||||
|
retryTask: vi.fn(),
|
||||||
|
resetTask: vi.fn(),
|
||||||
|
updateTask: vi.fn(),
|
||||||
|
duplicateTask: vi.fn(),
|
||||||
|
archiveTask: vi.fn(),
|
||||||
|
unarchiveTask: vi.fn(),
|
||||||
|
archiveAllDone: vi.fn(),
|
||||||
|
loadArchivedTasks: vi.fn(),
|
||||||
|
refreshTasks: vi.fn(),
|
||||||
|
ingestCreatedTasks: vi.fn(),
|
||||||
|
lastFetchTimeMs: Date.now(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { rerender } = render(<App />);
|
||||||
|
|
||||||
|
const progressBar = screen.getByRole("progressbar", { name: "Loading" });
|
||||||
|
expect(progressBar).toHaveAttribute("aria-busy", "true");
|
||||||
|
expect(progressBar).toHaveAttribute("data-visible", "true");
|
||||||
|
|
||||||
|
taskHookState.isStale = false;
|
||||||
|
rerender(<App />);
|
||||||
|
|
||||||
|
expect(progressBar).toHaveAttribute("aria-busy", "false");
|
||||||
|
expect(progressBar).toHaveAttribute("data-visible", "false");
|
||||||
|
});
|
||||||
|
|
||||||
it("FN-4250: ChatView branch is inside FileBrowserProvider", async () => {
|
it("FN-4250: ChatView branch is inside FileBrowserProvider", async () => {
|
||||||
localStorage.setItem(taskViewStorageKey(), "chat");
|
localStorage.setItem(taskViewStorageKey(), "chat");
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,17 @@ describe("TopProgressBar", () => {
|
|||||||
expect(progressBar).toHaveAttribute("aria-busy", "false");
|
expect(progressBar).toHaveAttribute("aria-busy", "false");
|
||||||
expect(progressBar).toHaveAttribute("data-visible", "false");
|
expect(progressBar).toHaveAttribute("data-visible", "false");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("derives visibility only from the visible prop across rerenders", () => {
|
||||||
|
const { rerender } = render(<TopProgressBar visible={true} />);
|
||||||
|
|
||||||
|
const progressBar = screen.getByRole("progressbar", { name: "Loading" });
|
||||||
|
expect(progressBar).toHaveAttribute("aria-busy", "true");
|
||||||
|
expect(progressBar).toHaveAttribute("data-visible", "true");
|
||||||
|
|
||||||
|
rerender(<TopProgressBar visible={false} />);
|
||||||
|
|
||||||
|
expect(progressBar).toHaveAttribute("aria-busy", "false");
|
||||||
|
expect(progressBar).toHaveAttribute("data-visible", "false");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user