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

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new b1a3d3bc209 Unify grid and graph view tooltips with dates, duration, 
and child states (#62119)
b1a3d3bc209 is described below

commit b1a3d3bc209ae5c00742a85e53dc7b55904744c4
Author: Nathan Hadfield <[email protected]>
AuthorDate: Fri Feb 20 13:38:22 2026 +0000

    Unify grid and graph view tooltips with dates, duration, and child states 
(#62119)
    
    Both grid and graph views now use the shared TaskInstanceTooltip component,
    showing consistent information: task ID, state, dates, duration, and child
    state breakdown. Previously the grid view used a basic tooltip without
    duration or child states, and the graph view was missing dates entirely
    because the API was overriding them with null for leaf tasks.
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../airflow/api_fastapi/core_api/routes/ui/grid.py |   6 +-
 .../airflow/ui/src/components/Graph/TaskNode.tsx   |   2 +
 .../ui/src/components/Graph/reactflowUtils.ts      |   1 +
 .../ui/src/components/Graph/useGraphLayout.ts      |   1 +
 .../ui/src/components/TaskInstanceTooltip.test.tsx | 144 +++++++++++++++++++++
 .../ui/src/components/TaskInstanceTooltip.tsx      | 123 +++++++++++-------
 .../airflow/ui/src/layouts/Details/Grid/GridTI.tsx |  84 +++++-------
 .../src/airflow/ui/src/utils/datetimeUtils.test.ts |   7 +
 .../src/airflow/ui/src/utils/datetimeUtils.ts      |  14 +-
 .../api_fastapi/core_api/routes/ui/test_grid.py    |  32 ++---
 10 files changed, 289 insertions(+), 125 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
index 694f12e0294..9c7436ba462 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
@@ -401,8 +401,6 @@ def get_grid_ti_summaries(
                 yielded_task_ids.add(node["task_id"])
                 if node["type"] == "task":
                     node["child_states"] = None
-                    node["min_start_date"] = None
-                    node["max_end_date"] = None
             yield node
 
         # For good history: add synthetic leaf nodes for task_ids that have 
TIs in this run
@@ -418,10 +416,8 @@ def get_grid_ti_summaries(
                 "type": "task",
                 "parent_id": None,
                 **agg,
-                # Align with leaf behavior
+                # Leaf tasks have no children
                 "child_states": None,
-                "min_start_date": None,
-                "max_end_date": None,
             }
 
     task_instances = list(get_node_sumaries())
diff --git a/airflow-core/src/airflow/ui/src/components/Graph/TaskNode.tsx 
b/airflow-core/src/airflow/ui/src/components/Graph/TaskNode.tsx
index a81fd9b773d..330528a4d82 100644
--- a/airflow-core/src/airflow/ui/src/components/Graph/TaskNode.tsx
+++ b/airflow-core/src/airflow/ui/src/components/Graph/TaskNode.tsx
@@ -43,6 +43,7 @@ export const TaskNode = ({
     operator,
     setupTeardownType,
     taskInstance,
+    tooltip,
     width = 0,
   },
   id,
@@ -89,6 +90,7 @@ export const TaskNode = ({
             placement: "top-start",
           }}
           taskInstance={taskInstance}
+          tooltip={isGroup ? tooltip : undefined}
         >
           <Flex
             // Alternate background color for nested open groups
diff --git a/airflow-core/src/airflow/ui/src/components/Graph/reactflowUtils.ts 
b/airflow-core/src/airflow/ui/src/components/Graph/reactflowUtils.ts
index f64951f861b..d73eec056ee 100644
--- a/airflow-core/src/airflow/ui/src/components/Graph/reactflowUtils.ts
+++ b/airflow-core/src/airflow/ui/src/components/Graph/reactflowUtils.ts
@@ -37,6 +37,7 @@ export type CustomNodeProps = {
   operator?: string | null;
   setupTeardownType?: NodeResponse["setup_teardown_type"];
   taskInstance?: LightGridTaskInstanceSummary;
+  tooltip?: string | null;
   type: string;
   width?: number;
 };
diff --git a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts 
b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts
index 83b415ad141..a340ddd47a6 100644
--- a/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts
+++ b/airflow-core/src/airflow/ui/src/components/Graph/useGraphLayout.ts
@@ -219,6 +219,7 @@ const generateElkGraph = ({
       layoutOptions: direction === "RIGHT" ? { "elk.portConstraints": 
"FIXED_SIDE" } : undefined,
       operator: node.operator,
       setupTeardownType: node.setup_teardown_type,
+      tooltip: node.tooltip,
       type: node.type,
       width,
     };
diff --git 
a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx 
b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
new file mode 100644
index 00000000000..bd2758c8bce
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
@@ -0,0 +1,144 @@
+/* eslint-disable unicorn/no-null */
+
+/*!
+ * 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 "@testing-library/jest-dom";
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+
+import type { LightGridTaskInstanceSummary } from "openapi/requests/types.gen";
+import { Wrapper } from "src/utils/Wrapper";
+
+import TaskInstanceTooltip from "./TaskInstanceTooltip";
+
+describe("TaskInstanceTooltip", () => {
+  it("renders children directly when both taskInstance and tooltip are 
undefined", () => {
+    render(
+      <TaskInstanceTooltip>
+        <span data-testid="child">Child content</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByTestId("child")).toBeInTheDocument();
+    // Should not render a tooltip trigger wrapper
+    expect(screen.queryByRole("tooltip")).toBeNull();
+  });
+
+  it("shows state and dates for LightGridTaskInstanceSummary with both dates", 
() => {
+    const taskInstance: LightGridTaskInstanceSummary = {
+      child_states: null,
+      max_end_date: "2025-01-01T01:00:00Z",
+      min_start_date: "2025-01-01T00:00:00Z",
+      state: "success",
+      task_display_name: "My Task",
+      task_id: "my_task",
+    };
+
+    render(
+      <TaskInstanceTooltip open taskInstance={taskInstance}>
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText(/state/iu)).toBeInTheDocument();
+    expect(screen.getByText(/startDate/iu)).toBeInTheDocument();
+    expect(screen.getByText(/endDate/iu)).toBeInTheDocument();
+    expect(screen.getByText(/duration/iu)).toBeInTheDocument();
+  });
+
+  it("shows only start date when max_end_date is null", () => {
+    const taskInstance: LightGridTaskInstanceSummary = {
+      child_states: null,
+      max_end_date: null,
+      min_start_date: "2025-01-01T00:00:00Z",
+      state: "running",
+      task_display_name: "My Task",
+      task_id: "my_task",
+    };
+
+    render(
+      <TaskInstanceTooltip open taskInstance={taskInstance}>
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText(/state/iu)).toBeInTheDocument();
+    expect(screen.getByText(/startDate/iu)).toBeInTheDocument();
+    expect(screen.queryByText(/endDate/iu)).toBeNull();
+    expect(screen.queryByText(/duration/iu)).toBeNull();
+  });
+
+  it("shows no dates when min_start_date is null", () => {
+    const taskInstance: LightGridTaskInstanceSummary = {
+      child_states: null,
+      max_end_date: null,
+      min_start_date: null,
+      state: "queued",
+      task_display_name: "My Task",
+      task_id: "my_task",
+    };
+
+    render(
+      <TaskInstanceTooltip open taskInstance={taskInstance}>
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText(/state/iu)).toBeInTheDocument();
+    expect(screen.queryByText(/startDate/iu)).toBeNull();
+    expect(screen.queryByText(/endDate/iu)).toBeNull();
+  });
+
+  it("shows tooltip text when only tooltip prop is provided", () => {
+    render(
+      <TaskInstanceTooltip open tooltip="My group description">
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText("My group description")).toBeInTheDocument();
+  });
+
+  it("shows both tooltip text and task instance data", () => {
+    const taskInstance: LightGridTaskInstanceSummary = {
+      child_states: { success: 3 },
+      max_end_date: "2025-01-01T02:00:00Z",
+      min_start_date: "2025-01-01T00:00:00Z",
+      state: "success",
+      task_display_name: "Group Task",
+      task_id: "group_task",
+    };
+
+    render(
+      <TaskInstanceTooltip open taskInstance={taskInstance} tooltip="Task 
Group Info">
+        <span>trigger</span>
+      </TaskInstanceTooltip>,
+      { wrapper: Wrapper },
+    );
+
+    expect(screen.getByText("Task Group Info")).toBeInTheDocument();
+    expect(screen.getAllByText(/state/iu).length).toBeGreaterThan(0);
+    expect(screen.getByText(/startDate/iu)).toBeInTheDocument();
+  });
+});
diff --git a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx 
b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
index 6dbec7a6b4b..a375252f420 100644
--- a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
@@ -26,78 +26,107 @@ import type {
 } from "openapi/requests/types.gen";
 import Time from "src/components/Time";
 import { Tooltip, type TooltipProps } from "src/components/ui";
-import { renderDuration, sortStateEntries } from "src/utils";
+import { getDuration, renderDuration, sortStateEntries } from "src/utils";
 
 type Props = {
   readonly taskInstance?: LightGridTaskInstanceSummary | 
TaskInstanceHistoryResponse | TaskInstanceResponse;
+  readonly tooltip?: string | null;
 } & Omit<TooltipProps, "content">;
 
-const TaskInstanceTooltip = ({ children, positioning, taskInstance, ...rest }: 
Props) => {
+const TaskInstanceTooltip = ({ children, positioning, taskInstance, tooltip, 
...rest }: Props) => {
   const { t: translate } = useTranslation("common");
 
+  const hasTooltip = tooltip !== undefined && tooltip !== null;
+
   const childEntries =
     taskInstance !== undefined && "child_states" in taskInstance && 
taskInstance.child_states !== null
       ? sortStateEntries(taskInstance.child_states)
       : [];
 
-  return taskInstance === undefined ? (
+  return taskInstance === undefined && !hasTooltip ? (
     children
   ) : (
     <Tooltip
       {...rest}
       content={
-        <Box>
-          <Text>
-            {translate("state")}:{" "}
-            {taskInstance.state
-              ? translate(`common:states.${taskInstance.state}`)
-              : translate("common:states.no_status")}
-          </Text>
-          {childEntries.length > 0 ? (
-            <VStack align="start" gap={1} mt={1}>
-              {childEntries.map(([state, count]) => (
-                <HStack gap={2} key={state}>
-                  <Box
-                    bg={`${state}.solid`}
-                    border="1px solid"
-                    borderColor="border.emphasized"
-                    borderRadius="2px"
-                    height="10px"
-                    width="10px"
-                  />
-                  <Text fontSize="xs">
-                    {count} {translate(`common:states.${state}`)}
-                  </Text>
-                </HStack>
-              ))}
-            </VStack>
-          ) : undefined}
-          {"dag_run_id" in taskInstance ? (
-            <Text>
-              {translate("runId")}: {taskInstance.dag_run_id}
-            </Text>
-          ) : undefined}
-          {"start_date" in taskInstance ? (
+        <VStack align="start" gap={1}>
+          {hasTooltip ? <Text fontWeight="bold">{tooltip}</Text> : undefined}
+          {taskInstance ? (
             <>
-              {taskInstance.try_number > 1 && (
-                <Text>
-                  {translate("tryNumber")}: {taskInstance.try_number}
-                </Text>
-              )}
-              <Text>
-                {translate("startDate")}: <Time 
datetime={taskInstance.start_date} />
-              </Text>
               <Text>
-                {translate("endDate")}: <Time datetime={taskInstance.end_date} 
/>
+                {translate("taskId")}: {taskInstance.task_id}
               </Text>
               <Text>
-                {translate("duration")}: 
{renderDuration(taskInstance.duration)}
+                {translate("state")}:{" "}
+                {taskInstance.state
+                  ? translate(`common:states.${taskInstance.state}`)
+                  : translate("common:states.no_status")}
               </Text>
+              {"dag_run_id" in taskInstance ? (
+                <Text>
+                  {translate("runId")}: {taskInstance.dag_run_id}
+                </Text>
+              ) : undefined}
+              {"start_date" in taskInstance ? (
+                <>
+                  {taskInstance.try_number > 1 && (
+                    <Text>
+                      {translate("tryNumber")}: {taskInstance.try_number}
+                    </Text>
+                  )}
+                  <Text>
+                    {translate("startDate")}: <Time 
datetime={taskInstance.start_date} />
+                  </Text>
+                  <Text>
+                    {translate("endDate")}: <Time 
datetime={taskInstance.end_date} />
+                  </Text>
+                  <Text>
+                    {translate("duration")}: 
{renderDuration(taskInstance.duration)}
+                  </Text>
+                </>
+              ) : undefined}
+              {"min_start_date" in taskInstance && taskInstance.min_start_date 
!== null ? (
+                <>
+                  <Text>
+                    {translate("startDate")}: <Time 
datetime={taskInstance.min_start_date} />
+                  </Text>
+                  {taskInstance.max_end_date !== null && (
+                    <>
+                      <Text>
+                        {translate("endDate")}: <Time 
datetime={taskInstance.max_end_date} />
+                      </Text>
+                      <Text>
+                        {translate("duration")}:{" "}
+                        {getDuration(taskInstance.min_start_date, 
taskInstance.max_end_date, false)}
+                      </Text>
+                    </>
+                  )}
+                </>
+              ) : undefined}
+              {childEntries.length > 0 ? (
+                <VStack align="start" gap={1} ps={2}>
+                  {childEntries.map(([state, count]) => (
+                    <HStack gap={2} key={state}>
+                      <Box
+                        bg={`${state}.solid`}
+                        border="1px solid"
+                        borderColor="border.emphasized"
+                        borderRadius="2px"
+                        height="10px"
+                        width="10px"
+                      />
+                      <Text fontSize="xs">
+                        {count} {translate(`common:states.${state}`)}
+                      </Text>
+                    </HStack>
+                  ))}
+                </VStack>
+              ) : undefined}
             </>
           ) : undefined}
-        </Box>
+        </VStack>
       }
-      key={taskInstance.task_id}
+      key={taskInstance?.task_id}
       portalled
       positioning={{
         offset: {
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
index dd5280ea9be..1a4b1413f21 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
@@ -16,14 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { Badge, Flex } from "@chakra-ui/react";
-import { useTranslation } from "react-i18next";
+import { Badge, Box, Flex } from "@chakra-ui/react";
 import { Link, useLocation, useParams, useSearchParams } from 
"react-router-dom";
 
 import type { LightGridTaskInstanceSummary } from "openapi/requests/types.gen";
-import { BasicTooltip } from "src/components/BasicTooltip";
 import { StateIcon } from "src/components/StateIcon";
-import Time from "src/components/Time";
+import TaskInstanceTooltip from "src/components/TaskInstanceTooltip";
 import { useHover } from "src/context/hover";
 import { buildTaskInstanceUrl } from "src/utils/links";
 
@@ -41,7 +39,6 @@ type Props = {
 export const GridTI = ({ dagId, instance, isGroup, isMapped, onClick, runId, 
taskId }: Props) => {
   const { hoveredTaskId, setHoveredTaskId } = useHover();
   const { groupId: selectedGroupId, taskId: selectedTaskId } = useParams();
-  const { t: translate } = useTranslation();
   const location = useLocation();
 
   const [searchParams] = useSearchParams();
@@ -83,56 +80,35 @@ export const GridTI = ({ dagId, instance, isGroup, 
isMapped, onClick, runId, tas
       py={0}
       transition="background-color 0.2s"
     >
-      <BasicTooltip
-        content={
-          <>
-            {translate("taskId")}: {taskId}
-            <br />
-            {translate("state")}:{" "}
-            {instance.state
-              ? translate(`common:states.${instance.state}`)
-              : translate("common:states.no_status")}
-            {instance.min_start_date !== null && (
-              <>
-                <br />
-                {translate("startDate")}: <Time 
datetime={instance.min_start_date} />
-              </>
-            )}
-            {instance.max_end_date !== null && (
-              <>
-                <br />
-                {translate("endDate")}: <Time datetime={instance.max_end_date} 
/>
-              </>
-            )}
-          </>
-        }
-      >
-        <Link
-          id={`grid-${runId}-${taskId}`}
-          onClick={onClick}
-          replace
-          to={{
-            pathname: taskUrl,
-            search: redirectionSearch,
-          }}
-        >
-          <Badge
-            alignItems="center"
-            borderRadius={4}
-            colorPalette={instance.state ?? "none"}
-            data-testid="task-state-badge"
-            display="flex"
-            height="14px"
-            justifyContent="center"
-            minH={0}
-            p={0}
-            variant="solid"
-            width="14px"
+      <TaskInstanceTooltip openDelay={500} positioning={{ placement: "bottom" 
}} taskInstance={instance}>
+        <Box as="span" display="inline-block">
+          <Link
+            id={`grid-${runId}-${taskId}`}
+            onClick={onClick}
+            replace
+            to={{
+              pathname: taskUrl,
+              search: redirectionSearch,
+            }}
           >
-            <StateIcon size={10} state={instance.state} />
-          </Badge>
-        </Link>
-      </BasicTooltip>
+            <Badge
+              alignItems="center"
+              borderRadius={4}
+              colorPalette={instance.state ?? "none"}
+              data-testid="task-state-badge"
+              display="flex"
+              height="14px"
+              justifyContent="center"
+              minH={0}
+              p={0}
+              variant="solid"
+              width="14px"
+            >
+              <StateIcon size={10} state={instance.state} />
+            </Badge>
+          </Link>
+        </Box>
+      </TaskInstanceTooltip>
     </Flex>
   );
 };
diff --git a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts 
b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts
index c8faa00b0c9..0f9ba62c67c 100644
--- a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts
+++ b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.test.ts
@@ -49,6 +49,13 @@ describe("getDuration", () => {
     expect(getDuration(start, end)).toBe("02:30:00");
   });
 
+  it("omits milliseconds when withMilliseconds is false", () => {
+    const start = "2024-03-14T10:00:00.000Z";
+    const end = "2024-03-14T10:00:05.511Z";
+
+    expect(getDuration(start, end, false)).toBe("00:00:05");
+  });
+
   it("handles small, null or undefined values", () => {
     // eslint-disable-next-line unicorn/no-null
     expect(getDuration(null, null)).toBe(undefined);
diff --git a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts 
b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
index ca251b8575a..1fe6b65ef88 100644
--- a/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
+++ b/airflow-core/src/airflow/ui/src/utils/datetimeUtils.ts
@@ -47,10 +47,18 @@ export const renderDuration = (
     : dayjs.duration(durationSeconds, "seconds").format("D[d]HH:mm:ss");
 };
 
-export const getDuration = (startDate?: string | null, endDate?: string | 
null) => {
-  const seconds = dayjs.duration(dayjs(endDate ?? undefined).diff(startDate ?? 
undefined)).asSeconds();
+export const getDuration = (
+  startDate?: string | null,
+  endDate?: string | null,
+  withMilliseconds: boolean = true,
+) => {
+  if (startDate === undefined || startDate === null || endDate === undefined 
|| endDate === null) {
+    return undefined;
+  }
+
+  const seconds = dayjs.duration(dayjs(endDate).diff(startDate)).asSeconds();
 
-  return renderDuration(seconds);
+  return renderDuration(seconds, withMilliseconds);
 };
 
 export const formatDate = (
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index e8ccafaa32d..fd67e7e3267 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -698,24 +698,24 @@ class TestGetGridDataEndpoint:
                     "task_id": "t1",
                     "task_display_name": "t1",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:00Z",
+                    "min_start_date": "2025-03-01T23:59:58Z",
                 },
                 {
                     "state": "success",
                     "task_id": "t2",
                     "task_display_name": "t2",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:02Z",
+                    "min_start_date": "2025-03-02T00:00:00Z",
                 },
                 {
                     "state": "success",
                     "task_id": "t7",
                     "task_display_name": "t7",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:04Z",
+                    "min_start_date": "2025-03-02T00:00:02Z",
                 },
                 {
                     "child_states": {"success": 4},
@@ -730,8 +730,8 @@ class TestGetGridDataEndpoint:
                     "task_id": "task_group-1.t6",
                     "task_display_name": "task_group-1.t6",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:06Z",
+                    "min_start_date": "2025-03-02T00:00:04Z",
                 },
                 {
                     "child_states": {"success": 3},
@@ -746,24 +746,24 @@ class TestGetGridDataEndpoint:
                     "task_id": "task_group-1.task_group-2.t3",
                     "task_display_name": "task_group-1.task_group-2.t3",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:08Z",
+                    "min_start_date": "2025-03-02T00:00:06Z",
                 },
                 {
                     "state": "success",
                     "task_id": "task_group-1.task_group-2.t4",
                     "task_display_name": "task_group-1.task_group-2.t4",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:10Z",
+                    "min_start_date": "2025-03-02T00:00:08Z",
                 },
                 {
                     "state": "success",
                     "task_id": "task_group-1.task_group-2.t5",
                     "task_display_name": "task_group-1.task_group-2.t5",
                     "child_states": None,
-                    "max_end_date": None,
-                    "min_start_date": None,
+                    "max_end_date": "2025-03-02T00:00:12Z",
+                    "min_start_date": "2025-03-02T00:00:10Z",
                 },
             ],
         }
@@ -812,8 +812,8 @@ class TestGetGridDataEndpoint:
                 "task_id": "mapped_task_group.subtask",
                 "task_display_name": "mapped_task_group.subtask",
                 "child_states": None,
-                "max_end_date": None,
-                "min_start_date": None,
+                "max_end_date": "2024-12-30T01:02:03Z",
+                "min_start_date": "2024-12-30T01:00:00Z",
             },
             {
                 "state": "success",

Reply via email to