Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions airflow-core/src/airflow/ui/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default defineConfig({
forbidOnly: process.env.CI !== undefined && process.env.CI !== "",
fullyParallel: true,
globalSetup: "./tests/e2e/global-setup.ts",
globalTeardown: "./tests/e2e/global-teardown.ts",
projects: [
{
name: "chromium",
Expand Down
66 changes: 66 additions & 0 deletions airflow-core/src/airflow/ui/tests/e2e/fixtures/asset-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*!
* 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.
*/

/**
* Asset data fixture — triggers asset_produces_1 DAG and waits for success.
*/

/* eslint-disable react-hooks/rules-of-hooks -- Playwright's `use` is not a React Hook. */
import { test as base } from "tests/e2e/fixtures";
import {
safeCleanupDagRun,
apiTriggerDagRun,
waitForDagReady,
waitForDagRunStatus,
} from "tests/e2e/utils/test-helpers";

export type AssetData = {
dagId: string;
};

export const test = base.extend<Record<never, never>, { assetData: AssetData }>({
assetData: [
async ({ authenticatedRequest }, use, _workerInfo) => {
const dagId = "asset_produces_1";
let createdRunId: string | undefined;

try {
await waitForDagReady(authenticatedRequest, dagId);
await authenticatedRequest.patch(`/api/v2/dags/${dagId}`, { data: { is_paused: false } });
const { dagRunId } = await apiTriggerDagRun(authenticatedRequest, dagId);

createdRunId = dagRunId;

await waitForDagRunStatus(authenticatedRequest, {
dagId,
expectedState: "success",
runId: dagRunId,
timeout: 120_000,
});

await use({ dagId });
} finally {
if (createdRunId !== undefined) {
await safeCleanupDagRun(authenticatedRequest, dagId, createdRunId);
}
}
},
{ scope: "worker", timeout: 180_000 },
],
});
Original file line number Diff line number Diff line change
@@ -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.
*/

/**
* Audit log data fixture — triggers DAG runs to generate audit log entries.
*/

/* eslint-disable react-hooks/rules-of-hooks -- Playwright's `use` is not a React Hook. */
import { testConfig } from "playwright.config";
import { test as base } from "tests/e2e/fixtures";
import {
safeCleanupDagRun,
apiTriggerDagRun,
waitForDagReady,
waitForDagRunStatus,
} from "tests/e2e/utils/test-helpers";

export type AuditLogData = {
dagId: string;
};

export const test = base.extend<Record<never, never>, { auditLogData: AuditLogData }>({
auditLogData: [
async ({ authenticatedRequest }, use, _workerInfo) => {
const dagId = testConfig.testDag.id;
const createdRunIds: Array<string> = [];

try {
await waitForDagReady(authenticatedRequest, dagId);
await authenticatedRequest.patch(`/api/v2/dags/${dagId}`, { data: { is_paused: false } });

for (let i = 0; i < 3; i++) {
const { dagRunId } = await apiTriggerDagRun(authenticatedRequest, dagId);

createdRunIds.push(dagRunId);
await waitForDagRunStatus(authenticatedRequest, {
dagId,
expectedState: "success",
runId: dagRunId,
timeout: 60_000,
});
}

await use({ dagId });
} finally {
for (const runId of createdRunIds) {
await safeCleanupDagRun(authenticatedRequest, dagId, runId);
}
}
},
{ scope: "worker", timeout: 180_000 },
],
});
89 changes: 89 additions & 0 deletions airflow-core/src/airflow/ui/tests/e2e/fixtures/calendar-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*!
* 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.
*/

/**
* Calendar data fixture — creates success + failed DAG runs for calendar tests.
*/

/* eslint-disable react-hooks/rules-of-hooks -- Playwright's `use` is not a React Hook. */
import dayjs from "dayjs";
import { testConfig } from "playwright.config";
import { test as base } from "tests/e2e/fixtures";
import {
apiCreateDagRun,
safeCleanupDagRun,
apiSetDagRunState,
uniqueRunId,
waitForDagReady,
} from "tests/e2e/utils/test-helpers";

export type CalendarRunsData = {
dagId: string;
};

export const test = base.extend<Record<never, never>, { calendarRunsData: CalendarRunsData }>({
calendarRunsData: [
async ({ authenticatedRequest }, use, workerInfo) => {
const dagId = testConfig.testDag.id;
const createdRunIds: Array<string> = [];

await waitForDagReady(authenticatedRequest, dagId);

const now = dayjs();
const yesterday = now.subtract(1, "day");
const baseDate = yesterday.isSame(now, "month") ? yesterday : now;

const workerHourOffset = workerInfo.parallelIndex * 2;
const successIso = baseDate
.startOf("day")
.hour(2 + workerHourOffset)
.toISOString();
const failedIso = baseDate
.startOf("day")
.hour(3 + workerHourOffset)
.toISOString();

const successRunId = uniqueRunId("cal_success");
const failedRunId = uniqueRunId("cal_failed");

try {
await apiCreateDagRun(authenticatedRequest, dagId, {
dag_run_id: successRunId,
logical_date: successIso,
});
createdRunIds.push(successRunId);
await apiSetDagRunState(authenticatedRequest, { dagId, runId: successRunId, state: "success" });

await apiCreateDagRun(authenticatedRequest, dagId, {
dag_run_id: failedRunId,
logical_date: failedIso,
});
createdRunIds.push(failedRunId);
await apiSetDagRunState(authenticatedRequest, { dagId, runId: failedRunId, state: "failed" });

await use({ dagId });
} finally {
for (const runId of createdRunIds) {
await safeCleanupDagRun(authenticatedRequest, dagId, runId);
}
}
},
{ scope: "worker", timeout: 180_000 },
],
});
91 changes: 91 additions & 0 deletions airflow-core/src/airflow/ui/tests/e2e/fixtures/dag-runs-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*!
* 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.
*/

/**
* DAG Runs page data fixture — creates runs across two DAGs for filtering tests.
*/

/* eslint-disable react-hooks/rules-of-hooks -- Playwright's `use` is not a React Hook. */
import { testConfig } from "playwright.config";
import { test as base } from "tests/e2e/fixtures";
import {
apiCreateDagRun,
safeCleanupDagRun,
apiSetDagRunState,
uniqueRunId,
waitForDagReady,
} from "tests/e2e/utils/test-helpers";

export type DagRunsPageData = {
dag1Id: string;
dag2Id: string;
};

export const test = base.extend<Record<never, never>, { dagRunsPageData: DagRunsPageData }>({
dagRunsPageData: [
async ({ authenticatedRequest }, use, workerInfo) => {
const dag1Id = testConfig.testDag.id;
const dag2Id = "example_python_operator";
const createdRuns: Array<{ dagId: string; runId: string }> = [];

try {
await Promise.all([
waitForDagReady(authenticatedRequest, dag1Id),
waitForDagReady(authenticatedRequest, dag2Id),
]);

const baseOffset = workerInfo.parallelIndex * 7_200_000;
const timestamp = Date.now() - baseOffset;

const runId1 = uniqueRunId("dagrun_failed");

await apiCreateDagRun(authenticatedRequest, dag1Id, {
dag_run_id: runId1,
logical_date: new Date(timestamp).toISOString(),
});
createdRuns.push({ dagId: dag1Id, runId: runId1 });
await apiSetDagRunState(authenticatedRequest, { dagId: dag1Id, runId: runId1, state: "failed" });

const runId2 = uniqueRunId("dagrun_success");

await apiCreateDagRun(authenticatedRequest, dag1Id, {
dag_run_id: runId2,
logical_date: new Date(timestamp + 60_000).toISOString(),
});
createdRuns.push({ dagId: dag1Id, runId: runId2 });
await apiSetDagRunState(authenticatedRequest, { dagId: dag1Id, runId: runId2, state: "success" });

const runId3 = uniqueRunId("dagrun_other");

await apiCreateDagRun(authenticatedRequest, dag2Id, {
dag_run_id: runId3,
logical_date: new Date(timestamp + 120_000).toISOString(),
});
createdRuns.push({ dagId: dag2Id, runId: runId3 });

await use({ dag1Id, dag2Id });
} finally {
for (const { dagId, runId } of createdRuns) {
await safeCleanupDagRun(authenticatedRequest, dagId, runId);
}
}
},
{ scope: "worker", timeout: 120_000 },
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*!
* 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.
*/

/**
* Dashboard data fixture — tracks UI-triggered DAG runs for cleanup.
*/
import { testConfig } from "playwright.config";
import { test as base } from "tests/e2e/fixtures";
import { safeCleanupDagRun } from "tests/e2e/utils/test-helpers";

export type DagRunCleanup = {
track: (runId: string) => void;
};

/* eslint-disable react-hooks/rules-of-hooks -- Playwright's `use` is not a React Hook. */
export const test = base.extend<{ dagRunCleanup: DagRunCleanup }>({
dagRunCleanup: async ({ authenticatedRequest }, use) => {
const trackedRunIds: Array<string> = [];

await use({ track: (runId: string) => trackedRunIds.push(runId) });

for (const runId of trackedRunIds) {
await safeCleanupDagRun(authenticatedRequest, testConfig.testDag.id, runId);
}
},
});
Loading
Loading