diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts b/airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts index b3e65857339e2..ab3291b820393 100644 --- a/airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts +++ b/airflow-core/src/airflow/ui/tests/e2e/pages/DagsPage.ts @@ -345,6 +345,48 @@ export class DagsPage extends BasePage { .waitFor({ state: "visible", timeout: 30_000 }); } + /** + * Open Dag Code tab using direct URL navigation + */ + public async openCodeTab(dagId: string): Promise { + await this.page.goto(`/dags/${dagId}/code`, { + waitUntil: "domcontentloaded", + }); + + const editor = this.getMonacoEditor(); + + // Wait for Monaco editor to mount + await editor.waitFor({ state: "visible", timeout: 30_000 }); + + // Wait for at least one rendered line (Monaco renders lazily) + await editor.locator(".view-line").first().waitFor({ + state: "visible", + timeout: 30_000, + }); + } + + /** + * Get DAG code text from Monaco editor + */ + public async getDagCodeText(): Promise { + const editor = this.getMonacoEditor(); + + // Monaco renders lines lazily; wait for at least one line + const lines = editor.locator(".view-line"); + await lines.first().waitFor({ state: "visible", timeout: 30_000 }); + + const contents = await lines.allTextContents(); + + return contents.join("\n"); + } + + /** + * Monaco editor root + */ + public getMonacoEditor() { + return this.page.locator(".monaco-editor"); + } + /** * Search for a Dag by name */ @@ -409,7 +451,6 @@ export class DagsPage extends BasePage { await this.page.waitForTimeout(500); await this.verifyTableViewVisible(); } - /** * Trigger a Dag run */ diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/dag-code-tab.spec.ts b/airflow-core/src/airflow/ui/tests/e2e/specs/dag-code-tab.spec.ts new file mode 100644 index 0000000000000..a489a6f1cb55a --- /dev/null +++ b/airflow-core/src/airflow/ui/tests/e2e/specs/dag-code-tab.spec.ts @@ -0,0 +1,69 @@ +/*! + * 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 { test, expect } from "@playwright/test"; +import { DagsPage } from "tests/e2e/pages/DagsPage"; +import { testConfig } from "playwright.config"; + +test.describe("Dag Code Tab", () => { + let dagsPage: DagsPage; + const testDagId = testConfig.testDag.id; + + test.beforeEach(async ({ page }) => { + dagsPage = new DagsPage(page); + }); + + test("verify Dag code displays", async () => { + await dagsPage.openCodeTab(testDagId); + + const codeText = await dagsPage.getDagCodeText(); + expect(codeText.trim().length).toBeGreaterThan(0); + }); + + test("verify syntax highlighting is applied", async () => { + await dagsPage.openCodeTab(testDagId); + + // Look for any token that has styling applied + const highlightedToken = dagsPage + .getMonacoEditor() + .locator(".view-line span[class], .view-line span[style]") + .first(); + + await expect(highlightedToken).toBeVisible(); + }); + + test("verify line numbers are displayed", async () => { + await dagsPage.openCodeTab(testDagId); + + const lineNumbers = dagsPage + .getMonacoEditor() + .locator(".margin-view-overlays"); + + await expect(lineNumbers).toBeVisible(); + }); + + test("verify code editor is scrollable", async () => { + await dagsPage.openCodeTab(testDagId); + + const scrollContainer = dagsPage + .getMonacoEditor() + .locator(".monaco-scrollable-element"); + + await expect(scrollContainer).toBeVisible(); + }); +});