This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-2-test by this push:
     new 376e74fc763 [v3-2-test] fix graph view not auto-refreshing task states 
during DAG run (#65518) (#65522)
376e74fc763 is described below

commit 376e74fc763497f7e90c1f7491e568e857dde256
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Apr 20 16:06:06 2026 +0530

    [v3-2-test] fix graph view not auto-refreshing task states during DAG run 
(#65518) (#65522)
    
    (cherry picked from commit 820e9d2a98ee9dcadfa67c91eb412445db9b6402)
    
    Co-authored-by: Rahul Vats <[email protected]>
---
 .../ui/src/layouts/Details/Graph/Graph.test.tsx    | 147 +++++++++++++++++++++
 .../airflow/ui/src/layouts/Details/Graph/Graph.tsx |  12 +-
 2 files changed, 157 insertions(+), 2 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.test.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.test.tsx
new file mode 100644
index 00000000000..8fa32dcc2c8
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.test.tsx
@@ -0,0 +1,147 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { render } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { useDagRunServiceGetDagRun } from "openapi/queries";
+import { useGridTiSummariesStream } from "src/queries/useGridTISummaries.ts";
+import { Wrapper } from "src/utils/Wrapper";
+
+import { Graph } from "./Graph";
+
+let mockParams: Record<string, string> = { dagId: "test_dag" };
+
+vi.mock("react-router-dom", async () => {
+  const actual = await vi.importActual("react-router-dom");
+
+  return {
+    ...actual,
+    useParams: () => mockParams,
+    useSearchParams: () => [new URLSearchParams()],
+  };
+});
+
+vi.mock("openapi/queries", () => ({
+  useDagRunServiceGetDagRun: vi.fn(() => ({ data: undefined, isLoading: false 
})),
+  useStructureServiceStructureData: vi.fn(() => ({ data: { edges: [], nodes: 
[] } })),
+}));
+
+vi.mock("src/queries/useDependencyGraph", () => ({
+  useDependencyGraph: vi.fn(() => ({ data: { edges: [], nodes: [] } })),
+}));
+
+vi.mock("src/components/Graph/useGraphLayout", () => ({
+  useGraphLayout: vi.fn(() => ({ data: { edges: [], nodes: [] } })),
+}));
+
+vi.mock("src/queries/useGridTISummaries.ts", () => ({
+  useGridTiSummariesStream: vi.fn(() => ({ summariesByRunId: new Map() })),
+}));
+
+vi.mock("src/hooks/useSelectedVersion", () => ({
+  default: vi.fn(() => 1),
+}));
+
+vi.mock("src/context/openGroups", () => ({
+  useOpenGroups: vi.fn(() => ({ allGroupIds: [], openGroupIds: [], 
setAllGroupIds: vi.fn() })),
+}));
+
+vi.mock("@xyflow/react", async () => {
+  const actual = await vi.importActual("@xyflow/react");
+
+  return {
+    ...actual,
+    ReactFlow: vi.fn(() => null),
+    useReactFlow: vi.fn(() => ({ fitView: vi.fn(), getZoom: vi.fn() })),
+  };
+});
+
+const mockDagRun = {
+  bundle_version: null,
+  conf: null,
+  dag_display_name: "test_dag",
+  dag_id: "test_dag",
+  dag_versions: [],
+  end_date: null,
+  has_missed_deadline: false,
+  logical_date: null,
+  note: null,
+  partition_key: null,
+  queued_at: null,
+  run_after: "2025-01-01T00:00:00Z",
+  run_id: "run_1",
+  run_type: "manual" as const,
+  start_date: "2025-01-01T00:00:01Z",
+  state: "running" as const,
+  triggered_by: "ui" as const,
+  triggering_user_name: null,
+};
+
+describe("Graph", () => {
+  it("passes states to useGridTiSummariesStream when a runId is present", () 
=> {
+    mockParams = { dagId: "test_dag", runId: "run_1" };
+    vi.mocked(useDagRunServiceGetDagRun).mockReturnValue({
+      data: mockDagRun,
+      isLoading: false,
+    } as ReturnType<typeof useDagRunServiceGetDagRun>);
+
+    render(<Graph />, { wrapper: Wrapper });
+
+    expect(vi.mocked(useGridTiSummariesStream)).toHaveBeenCalledWith(
+      expect.objectContaining({
+        runIds: ["run_1"],
+        states: ["running"],
+      }),
+    );
+  });
+
+  it("passes undefined states while dag run is still loading", () => {
+    mockParams = { dagId: "test_dag", runId: "run_1" };
+    vi.mocked(useDagRunServiceGetDagRun).mockReturnValue({
+      data: undefined,
+      isLoading: true,
+    } as ReturnType<typeof useDagRunServiceGetDagRun>);
+
+    render(<Graph />, { wrapper: Wrapper });
+
+    expect(vi.mocked(useGridTiSummariesStream)).toHaveBeenCalledWith(
+      expect.objectContaining({
+        runIds: ["run_1"],
+        states: undefined,
+      }),
+    );
+  });
+
+  it("passes undefined states to useGridTiSummariesStream when there is no 
runId", () => {
+    mockParams = { dagId: "test_dag" };
+    vi.mocked(useDagRunServiceGetDagRun).mockReturnValue({
+      data: undefined,
+      isLoading: false,
+    } as ReturnType<typeof useDagRunServiceGetDagRun>);
+
+    render(<Graph />, { wrapper: Wrapper });
+
+    expect(vi.mocked(useGridTiSummariesStream)).toHaveBeenCalledWith(
+      expect.objectContaining({
+        runIds: [],
+        states: undefined,
+      }),
+    );
+  });
+});
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx
index 6e3fe794021..fb16cf04c84 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Graph/Graph.tsx
@@ -23,7 +23,7 @@ import { useEffect } from "react";
 import { useParams, useSearchParams } from "react-router-dom";
 import { useLocalStorage } from "usehooks-ts";
 
-import { useStructureServiceStructureData } from "openapi/queries";
+import { useDagRunServiceGetDagRun, useStructureServiceStructureData } from 
"openapi/queries";
 import { DownloadButton } from "src/components/Graph/DownloadButton";
 import { edgeTypes, nodeTypes } from "src/components/Graph/graphTypes";
 import type { CustomNodeProps } from "src/components/Graph/reactflowUtils";
@@ -134,7 +134,15 @@ export const Graph = () => {
     versionNumber: selectedVersion,
   });
 
-  const { summariesByRunId } = useGridTiSummariesStream({ dagId, runIds: runId 
? [runId] : [] });
+  const { data: dagRun } = useDagRunServiceGetDagRun({ dagId, dagRunId: runId 
}, undefined, {
+    enabled: Boolean(runId),
+  });
+
+  const { summariesByRunId } = useGridTiSummariesStream({
+    dagId,
+    runIds: runId ? [runId] : [],
+    states: dagRun ? [dagRun.state] : undefined,
+  });
   const gridTISummaries = runId ? summariesByRunId.get(runId) : undefined;
 
   // Add task instances to the node data but without having to recalculate how 
the graph is laid out

Reply via email to