vatsrahul1001 commented on code in PR #59754:
URL: https://github.com/apache/airflow/pull/59754#discussion_r2649654710
##########
airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts:
##########
@@ -116,6 +121,115 @@ export class DagsPage extends BasePage {
/**
* Navigate to details tab and verify Dag details are displayed correctly
*/
+ /**
+ * Navigate to the Runs tab for a specific DAG
+ */
+ public async navigateToRunsTab(dagName: string): Promise<void> {
+ await this.navigateToDagDetail(dagName);
+ await this.runsTab.waitFor({ state: "visible" });
+ await this.runsTab.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Verify the Runs tab is displayed correctly
+ */
+ public async verifyRunsTabDisplayed(): Promise<void> {
+ // Verify the runs table is present
+ await expect(this.runsTable).toBeVisible();
+
+ // Verify pagination controls are visible
+ await expect(this.paginationNextButton).toBeVisible();
+ await expect(this.paginationPrevButton).toBeVisible();
+ }
+
+ /**
+ * Get run details from the runs table
+ */
+ public async getRunDetails(): Promise<
+ Array<{
+ runId: string;
+ state: string;
+ }>
+ > {
+ const runRows = this.page.locator('[data-testid="dag-runs-table"] table
tbody tr');
+ await runRows.first().waitFor({ state: "visible", timeout: 10_000 });
+
+ const runCount = await runRows.count();
+ const runs: Array<{ runId: string; state: string }> = [];
+
+ for (let i = 0; i < runCount; i++) {
+ const row = runRows.nth(i);
+ const cells = row.locator("td");
+
+ // Assuming first column is run ID and state column exists
+ const runId = (await cells.nth(0).textContent()) ?? "";
+ const state = (await cells.nth(1).textContent()) ?? "";
+
+ runs.push({ runId: runId.trim(), state: state.trim() });
+ }
+
+ return runs;
+ }
+
+ /**
+ * Click on a specific run to view details
+ */
+ public async clickRun(runId: string): Promise<void> {
+ const runLink = this.page.locator(`a:has-text("${runId}")`).first();
+
+ await runLink.waitFor({ state: "visible" });
+ await runLink.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Filter runs by state
+ */
+ public async filterByState(state: string): Promise<void> {
+ // Click on the state filter
+ const stateFilter = this.page.locator('button:has-text("State")');
+
+ await stateFilter.waitFor({ state: "visible" });
+ await stateFilter.click();
+
+ // Select the specific state
+ const stateOption =
this.page.locator(`[role="option"]:has-text("${state}")`);
+
+ await stateOption.waitFor({ state: "visible" });
+ await stateOption.click();
+
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Search for dag runs by run ID pattern
+ */
+ public async searchRun(searchTerm: string): Promise<void> {
+ // Find the run ID pattern input field
+ const searchInput = this.page.locator('input[placeholder*="Run ID"]').or(
+ this.page.locator('input[name="runIdPattern"]'),
+ );
+
+ await searchInput.waitFor({ state: "visible" });
+ await searchInput.fill(searchTerm);
+
+ // Wait for the search to take effect
+ await this.page.waitForTimeout(1000);
Review Comment:
Wait for a specific condition instead of arbitrary timeout.
##########
airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts:
##########
@@ -116,6 +121,115 @@ export class DagsPage extends BasePage {
/**
* Navigate to details tab and verify Dag details are displayed correctly
*/
+ /**
+ * Navigate to the Runs tab for a specific DAG
+ */
+ public async navigateToRunsTab(dagName: string): Promise<void> {
+ await this.navigateToDagDetail(dagName);
+ await this.runsTab.waitFor({ state: "visible" });
+ await this.runsTab.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Verify the Runs tab is displayed correctly
+ */
+ public async verifyRunsTabDisplayed(): Promise<void> {
+ // Verify the runs table is present
+ await expect(this.runsTable).toBeVisible();
+
+ // Verify pagination controls are visible
+ await expect(this.paginationNextButton).toBeVisible();
+ await expect(this.paginationPrevButton).toBeVisible();
+ }
+
+ /**
+ * Get run details from the runs table
+ */
+ public async getRunDetails(): Promise<
+ Array<{
+ runId: string;
+ state: string;
+ }>
+ > {
+ const runRows = this.page.locator('[data-testid="dag-runs-table"] table
tbody tr');
+ await runRows.first().waitFor({ state: "visible", timeout: 10_000 });
+
+ const runCount = await runRows.count();
+ const runs: Array<{ runId: string; state: string }> = [];
+
+ for (let i = 0; i < runCount; i++) {
+ const row = runRows.nth(i);
+ const cells = row.locator("td");
+
+ // Assuming first column is run ID and state column exists
+ const runId = (await cells.nth(0).textContent()) ?? "";
+ const state = (await cells.nth(1).textContent()) ?? "";
+
+ runs.push({ runId: runId.trim(), state: state.trim() });
+ }
+
+ return runs;
+ }
+
+ /**
+ * Click on a specific run to view details
+ */
+ public async clickRun(runId: string): Promise<void> {
+ const runLink = this.page.locator(`a:has-text("${runId}")`).first();
+
+ await runLink.waitFor({ state: "visible" });
+ await runLink.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Filter runs by state
+ */
+ public async filterByState(state: string): Promise<void> {
+ // Click on the state filter
+ const stateFilter = this.page.locator('button:has-text("State")');
+
+ await stateFilter.waitFor({ state: "visible" });
+ await stateFilter.click();
+
+ // Select the specific state
+ const stateOption =
this.page.locator(`[role="option"]:has-text("${state}")`);
+
+ await stateOption.waitFor({ state: "visible" });
+ await stateOption.click();
+
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Search for dag runs by run ID pattern
+ */
+ public async searchRun(searchTerm: string): Promise<void> {
Review Comment:
We are not using searchRun metod in any test
##########
airflow-core/src/airflow/ui/tests/e2e/specs/dags-list.spec.ts:
##########
@@ -84,3 +84,87 @@ test.describe("Dag Details Tab", () => {
await dagsPage.verifyDagDetails(testDagId);
});
});
+
+/*
+ * DAG Runs Tab E2E Tests
+ */
+test.describe("DAG Runs Tab", () => {
+ let dagsPage: DagsPage;
+
+ const testDagId = testConfig.testDag.id;
+
+ test.beforeEach(({ page }) => {
+ dagsPage = new DagsPage(page);
+ });
+
+ test("should navigate to Runs tab and display correctly", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ // Verify runs table is displayed
+ await dagsPage.verifyRunsTabDisplayed();
+
+ // Verify we can see run details
+ const runs = await dagsPage.getRunDetails();
+
+ expect(runs.length).toBeGreaterThan(0);
+ });
+
+ test("should verify run details are displayed correctly", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ const runs = await dagsPage.getRunDetails();
+
+ expect(runs.length).toBeGreaterThan(0);
Review Comment:
If the test DAG has no runs, all tests fail. Consider Triggering a DAG run
before testing
##########
airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts:
##########
@@ -116,6 +121,115 @@ export class DagsPage extends BasePage {
/**
* Navigate to details tab and verify Dag details are displayed correctly
*/
+ /**
+ * Navigate to the Runs tab for a specific DAG
+ */
+ public async navigateToRunsTab(dagName: string): Promise<void> {
+ await this.navigateToDagDetail(dagName);
+ await this.runsTab.waitFor({ state: "visible" });
+ await this.runsTab.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Verify the Runs tab is displayed correctly
+ */
+ public async verifyRunsTabDisplayed(): Promise<void> {
+ // Verify the runs table is present
+ await expect(this.runsTable).toBeVisible();
+
+ // Verify pagination controls are visible
+ await expect(this.paginationNextButton).toBeVisible();
+ await expect(this.paginationPrevButton).toBeVisible();
+ }
+
+ /**
+ * Get run details from the runs table
+ */
+ public async getRunDetails(): Promise<
+ Array<{
+ runId: string;
+ state: string;
+ }>
+ > {
+ const runRows = this.page.locator('[data-testid="dag-runs-table"] table
tbody tr');
+ await runRows.first().waitFor({ state: "visible", timeout: 10_000 });
+
+ const runCount = await runRows.count();
+ const runs: Array<{ runId: string; state: string }> = [];
+
+ for (let i = 0; i < runCount; i++) {
+ const row = runRows.nth(i);
+ const cells = row.locator("td");
+
+ // Assuming first column is run ID and state column exists
+ const runId = (await cells.nth(0).textContent()) ?? "";
Review Comment:
We could use better selector here
Assumes runId is column 0 and state is column 1. If UI changes column order,
test breaks.
##########
airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts:
##########
@@ -116,6 +121,115 @@ export class DagsPage extends BasePage {
/**
* Navigate to details tab and verify Dag details are displayed correctly
Review Comment:
The first comment is orphaned from a previous method. We can remove it.
##########
airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts:
##########
@@ -116,6 +121,115 @@ export class DagsPage extends BasePage {
/**
* Navigate to details tab and verify Dag details are displayed correctly
*/
+ /**
+ * Navigate to the Runs tab for a specific DAG
+ */
+ public async navigateToRunsTab(dagName: string): Promise<void> {
+ await this.navigateToDagDetail(dagName);
+ await this.runsTab.waitFor({ state: "visible" });
+ await this.runsTab.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Verify the Runs tab is displayed correctly
+ */
+ public async verifyRunsTabDisplayed(): Promise<void> {
+ // Verify the runs table is present
+ await expect(this.runsTable).toBeVisible();
+
+ // Verify pagination controls are visible
+ await expect(this.paginationNextButton).toBeVisible();
+ await expect(this.paginationPrevButton).toBeVisible();
+ }
+
+ /**
+ * Get run details from the runs table
+ */
+ public async getRunDetails(): Promise<
+ Array<{
+ runId: string;
+ state: string;
+ }>
+ > {
+ const runRows = this.page.locator('[data-testid="dag-runs-table"] table
tbody tr');
+ await runRows.first().waitFor({ state: "visible", timeout: 10_000 });
+
+ const runCount = await runRows.count();
+ const runs: Array<{ runId: string; state: string }> = [];
+
+ for (let i = 0; i < runCount; i++) {
+ const row = runRows.nth(i);
+ const cells = row.locator("td");
+
+ // Assuming first column is run ID and state column exists
+ const runId = (await cells.nth(0).textContent()) ?? "";
+ const state = (await cells.nth(1).textContent()) ?? "";
+
+ runs.push({ runId: runId.trim(), state: state.trim() });
+ }
+
+ return runs;
+ }
+
+ /**
+ * Click on a specific run to view details
+ */
+ public async clickRun(runId: string): Promise<void> {
+ const runLink = this.page.locator(`a:has-text("${runId}")`).first();
+
+ await runLink.waitFor({ state: "visible" });
+ await runLink.click();
+ await this.waitForPageLoad();
+ }
+
+ /**
+ * Filter runs by state
+ */
+ public async filterByState(state: string): Promise<void> {
Review Comment:
We are not using this method in any test
##########
airflow-core/src/airflow/ui/tests/e2e/specs/dags-list.spec.ts:
##########
@@ -84,3 +84,87 @@ test.describe("Dag Details Tab", () => {
await dagsPage.verifyDagDetails(testDagId);
});
});
+
+/*
+ * DAG Runs Tab E2E Tests
+ */
+test.describe("DAG Runs Tab", () => {
Review Comment:
Use Dag instead of DAG
##########
airflow-core/src/airflow/ui/tests/e2e/specs/dags-list.spec.ts:
##########
@@ -84,3 +84,87 @@ test.describe("Dag Details Tab", () => {
await dagsPage.verifyDagDetails(testDagId);
});
});
+
+/*
+ * DAG Runs Tab E2E Tests
+ */
+test.describe("DAG Runs Tab", () => {
+ let dagsPage: DagsPage;
+
+ const testDagId = testConfig.testDag.id;
+
+ test.beforeEach(({ page }) => {
+ dagsPage = new DagsPage(page);
+ });
+
+ test("should navigate to Runs tab and display correctly", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ // Verify runs table is displayed
+ await dagsPage.verifyRunsTabDisplayed();
+
+ // Verify we can see run details
+ const runs = await dagsPage.getRunDetails();
+
+ expect(runs.length).toBeGreaterThan(0);
+ });
+
+ test("should verify run details are displayed correctly", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ const runs = await dagsPage.getRunDetails();
+
+ expect(runs.length).toBeGreaterThan(0);
+
+ // Verify first run has required fields
+ const firstRun = runs[0];
+
+ expect(firstRun.runId).toBeTruthy();
+ expect(firstRun.state).toBeTruthy();
+ });
+
+ test("should click run and navigate to details page", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ const runs = await dagsPage.getRunDetails();
+
+ expect(runs.length).toBeGreaterThan(0);
+
+ const firstRun = runs[0];
+
+ await dagsPage.clickRun(firstRun.runId);
+
+ // Verify we're on the run details page
+ await dagsPage.verifyRunDetailsPage(firstRun.runId);
+ });
+
+ test("should verify pagination works on the Runs tab", async () => {
+ await dagsPage.navigateToRunsTab(testDagId);
+
+ await expect(dagsPage.paginationNextButton).toBeVisible();
+ await expect(dagsPage.paginationPrevButton).toBeVisible();
+
+ const initialRuns = await dagsPage.getRunDetails();
+
+ expect(initialRuns.length).toBeGreaterThan(0);
+
+ // Check if next button is enabled (has more than one page)
+ const isNextEnabled = await dagsPage.paginationNextButton.isEnabled();
+
+ if (isNextEnabled) {
Review Comment:
If there's only one page of results, the core pagination test is skipped
silently. Consider using test.skip() with a message.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]