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

rahulvats 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 323bc16c9b9 Test(UI): Add E2E tests for Asset Details (#59939)
323bc16c9b9 is described below

commit 323bc16c9b923c325c436a86a9f59af30833a987
Author: M Junaid Shaukat <[email protected]>
AuthorDate: Wed Jan 28 09:49:57 2026 +0500

    Test(UI): Add E2E tests for Asset Details (#59939)
    
    Add E2E tests for Asset Details
    
    Signed-off-by: junaiddshaukat <[email protected]>
    Co-authored-by: Rahul Vats <[email protected]>
---
 airflow-core/src/airflow/ui/playwright.config.ts   |  3 +
 .../airflow/ui/tests/e2e/pages/AssetDetailPage.ts  | 82 ++++++++++++++++++++++
 .../src/airflow/ui/tests/e2e/specs/asset.spec.ts   | 20 +++++-
 3 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/airflow-core/src/airflow/ui/playwright.config.ts 
b/airflow-core/src/airflow/ui/playwright.config.ts
index 3b2d8d7d103..bd8f753620f 100644
--- a/airflow-core/src/airflow/ui/playwright.config.ts
+++ b/airflow-core/src/airflow/ui/playwright.config.ts
@@ -21,6 +21,9 @@ import path from "node:path";
 import { fileURLToPath } from "node:url";
 
 export const testConfig = {
+  asset: {
+    name: process.env.TEST_ASSET_NAME ?? "s3://dag1/output_1.txt",
+  },
   credentials: {
     password: process.env.TEST_PASSWORD ?? "admin",
     username: process.env.TEST_USERNAME ?? "admin",
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts 
b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts
new file mode 100644
index 00000000000..9fd8b6f23f6
--- /dev/null
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/AssetDetailPage.ts
@@ -0,0 +1,82 @@
+/*!
+ * 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 { expect, type Page } from "@playwright/test";
+
+import { BasePage } from "./BasePage";
+
+export class AssetDetailPage extends BasePage {
+  public static get url(): string {
+    return "/assets";
+  }
+
+  public constructor(page: Page) {
+    super(page);
+  }
+
+  public async clickOnAsset(name: string): Promise<void> {
+    await this.page.getByRole("link", { exact: true, name }).click();
+  }
+
+  public async goto(): Promise<void> {
+    await this.navigateTo(AssetDetailPage.url);
+  }
+
+  public async verifyAssetDetails(name: string): Promise<void> {
+    await expect(this.page.getByRole("heading", { name })).toBeVisible();
+  }
+
+  public async verifyProducingTasks(minCount: number): Promise<void> {
+    await this.verifyStatSection("Producing Tasks", minCount);
+  }
+
+  public async verifyScheduledDags(minCount: number): Promise<void> {
+    await this.verifyStatSection("Scheduled Dags", minCount);
+  }
+
+  /**
+   * Common helper to verify stat sections (Producing Tasks, Scheduled Dags)
+   * Uses stable selectors based on text content and ARIA roles
+   */
+  private async verifyStatSection(labelText: string, minCount: number): 
Promise<void> {
+    const label = this.page.getByText(labelText, { exact: true });
+
+    await expect(label).toBeVisible();
+
+    // Find the button that follows the label in the same stat section
+    // Navigate to the label's parent container and find the first button 
within it
+    const statContainer = label.locator("xpath=./parent::*");
+    const button = statContainer.getByRole("button").first();
+
+    await expect(button).toBeVisible();
+    const text = await button.textContent();
+    const count = parseInt(text?.split(" ")[0] ?? "0", 10);
+
+    expect(count).toBeGreaterThanOrEqual(minCount);
+
+    if (count > 0) {
+      await button.click();
+      // Wait for popover and verify links using role-based selector
+      const popoverLinks = this.page.getByRole("dialog").getByRole("link");
+
+      await expect(popoverLinks).toHaveCount(count);
+      // Close popover
+      await this.page.keyboard.press("Escape");
+    }
+  }
+}
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts 
b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
index e1f05790fe9..a123b64b101 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/asset.spec.ts
@@ -17,8 +17,9 @@
  * under the License.
  */
 import { expect, test } from "@playwright/test";
-import { AUTH_FILE } from "playwright.config";
+import { AUTH_FILE, testConfig } from "playwright.config";
 
+import { AssetDetailPage } from "../pages/AssetDetailPage";
 import { AssetListPage } from "../pages/AssetListPage";
 import { DagsPage } from "../pages/DagsPage";
 
@@ -88,7 +89,7 @@ test.describe("Assets Page", () => {
 
     expect(initialCount).toBeGreaterThan(0);
 
-    const searchTerm = "s3://dag1/output_1.txt";
+    const searchTerm = testConfig.asset.name;
 
     await assets.searchInput.fill(searchTerm);
 
@@ -135,4 +136,19 @@ test.describe("Assets Page", () => {
 
     await expect.poll(() => assets.assetNames(), { timeout: 30_000 
}).not.toEqual(page2Assets);
   });
+
+  test("verify asset details and dependencies", async ({ page }) => {
+    const assetDetailPage = new AssetDetailPage(page);
+    const assetName = testConfig.asset.name;
+
+    await assetDetailPage.goto();
+
+    await assetDetailPage.clickOnAsset(assetName);
+
+    await assetDetailPage.verifyAssetDetails(assetName);
+
+    await assetDetailPage.verifyProducingTasks(1);
+
+    await assetDetailPage.verifyScheduledDags(1);
+  });
 });

Reply via email to