This is an automated email from the ASF dual-hosted git repository.
bbovenzi 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 717c0fb9e1e Fix 63573 task duration tooltip (#63639)
717c0fb9e1e is described below
commit 717c0fb9e1ede0ce3b60f08c5b74a7221cd73c09
Author: Subham <[email protected]>
AuthorDate: Wed Mar 18 23:41:23 2026 +0530
Fix 63573 task duration tooltip (#63639)
* Fix task duration tooltip showing stale duration for running tasks
(#63573)
* UI: fix TaskInstanceTooltip test typing
Made-with: Cursor
---------
Co-authored-by: Jarek Potiuk <[email protected]>
---
.../ui/src/components/TaskInstanceTooltip.test.tsx | 59 +++++++++++++++++++++-
.../ui/src/components/TaskInstanceTooltip.tsx | 8 ++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
index bd2758c8bce..51058868003 100644
--- a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.test.tsx
@@ -22,7 +22,7 @@ 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 type { LightGridTaskInstanceSummary, TaskInstanceResponse } from
"openapi/requests/types.gen";
import { Wrapper } from "src/utils/Wrapper";
import TaskInstanceTooltip from "./TaskInstanceTooltip";
@@ -60,10 +60,65 @@ describe("TaskInstanceTooltip", () => {
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("calculates live duration for a running task instead of using stale
duration", () => {
+ // We pass a stale duration of 50 seconds, but start date is 2 hours ago.
+ // The calculated duration should be ~2 hours, not 50 seconds.
+ const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 *
1000).toISOString();
+ const taskInstance: TaskInstanceResponse = {
+ dag_display_name: "Test DAG",
+ dag_id: "test",
+ dag_run_id: "test",
+ dag_version: null,
+ duration: 50.0,
+ end_date: null,
+ executor: null,
+ executor_config: "{}",
+ hostname: null,
+ id: "test_my_task",
+ logical_date: null,
+ map_index: 0,
+ max_tries: 3,
+ note: null,
+ operator: "DummyOperator",
+ operator_name: "DummyOperator",
+ pid: null,
+ pool: "default_pool",
+ pool_slots: 1,
+ priority_weight: null,
+ queue: null,
+ queued_when: null,
+ rendered_fields: undefined,
+ rendered_map_index: null,
+ run_after: twoHoursAgo,
+ scheduled_when: null,
+ start_date: twoHoursAgo,
+ state: "running",
+ task_display_name: "My Task",
+ task_id: "my_task",
+ trigger: null,
+ triggerer_job: null,
+ try_number: 2,
+ unixname: null,
+ };
+
+ render(
+ <TaskInstanceTooltip open taskInstance={taskInstance}>
+ <span>trigger</span>
+ </TaskInstanceTooltip>,
+ { wrapper: Wrapper },
+ );
+
+ const durationText =
screen.getByText(/duration/iu).parentElement?.textContent;
+
+ // The calculated duration should be around 2 hours (e.g., "02:00:00" or
similar)
+ // It should definitely NOT be "00:00:50.000" which is what
`renderDuration(50)` gives
+ expect(durationText).not.toContain("00:00:50");
+ expect(durationText).toContain("02:00:");
+ });
+
it("shows only start date when max_end_date is null", () => {
const taskInstance: LightGridTaskInstanceSummary = {
child_states: null,
diff --git a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
index a375252f420..8162c02a7c3 100644
--- a/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TaskInstanceTooltip.tsx
@@ -81,7 +81,13 @@ const TaskInstanceTooltip = ({ children, positioning,
taskInstance, tooltip, ...
{translate("endDate")}: <Time
datetime={taskInstance.end_date} />
</Text>
<Text>
- {translate("duration")}:
{renderDuration(taskInstance.duration)}
+ {translate("duration")}:{" "}
+ {taskInstance.state === "running" || taskInstance.state
=== "deferred"
+ ? getDuration(
+ taskInstance.start_date,
+ taskInstance.end_date ?? new Date().toISOString(),
+ )
+ : renderDuration(taskInstance.duration)}
</Text>
</>
) : undefined}