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 8f6b8551c3 Improve react www tests (#23329)
8f6b8551c3 is described below
commit 8f6b8551c325ff64f42080391a0f97b3a5d2e7d5
Author: Brent Bovenzi <[email protected]>
AuthorDate: Mon May 2 19:38:57 2022 -0400
Improve react www tests (#23329)
* Add shared test wrapper & treeData placeholder
* remove console log
* move testUtils to /utils
* change unnamed export
---
airflow/www/static/js/tree/Tree.jsx | 2 +-
airflow/www/static/js/tree/api/useTreeData.js | 23 +++--
.../www/static/js/tree/api/useTreeData.test.jsx | 16 +---
airflow/www/static/js/tree/dagRuns/index.jsx | 2 +-
airflow/www/static/js/tree/dagRuns/index.test.jsx | 99 ++++++----------------
airflow/www/static/js/tree/details/Header.jsx | 2 +-
airflow/www/static/js/tree/details/content/Dag.jsx | 2 +-
.../js/tree/details/content/dagRun/index.jsx | 2 +-
.../js/tree/details/content/taskInstance/index.jsx | 2 +-
airflow/www/static/js/tree/renderTaskRows.test.jsx | 33 +-------
airflow/www/static/js/tree/utils/testUtils.jsx | 67 +++++++++++++++
11 files changed, 121 insertions(+), 129 deletions(-)
diff --git a/airflow/www/static/js/tree/Tree.jsx
b/airflow/www/static/js/tree/Tree.jsx
index 1283d14c1b..b2d1c1dfb1 100644
--- a/airflow/www/static/js/tree/Tree.jsx
+++ b/airflow/www/static/js/tree/Tree.jsx
@@ -47,7 +47,7 @@ const sidePanelKey = 'hideSidePanel';
const Tree = () => {
const scrollRef = useRef();
const tableRef = useRef();
- const { data: { groups = {}, dagRuns = [] } } = useTreeData();
+ const { data: { groups, dagRuns } } = useTreeData();
const { isRefreshOn, toggleRefresh, isPaused } = useAutoRefresh();
const isPanelOpen = localStorage.getItem(sidePanelKey) !== 'true';
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: isPanelOpen });
diff --git a/airflow/www/static/js/tree/api/useTreeData.js
b/airflow/www/static/js/tree/api/useTreeData.js
index 9cb9737fd3..2a0468cac3 100644
--- a/airflow/www/static/js/tree/api/useTreeData.js
+++ b/airflow/www/static/js/tree/api/useTreeData.js
@@ -29,24 +29,30 @@ import useErrorToast from '../utils/useErrorToast';
// dagId comes from dag.html
const dagId = getMetaValue('dag_id');
-const treeDataUrl = getMetaValue('tree_data_url');
+const treeDataUrl = getMetaValue('tree_data_url') || '';
const numRuns = getMetaValue('num_runs');
const urlRoot = getMetaValue('root');
const baseDate = getMetaValue('base_date');
+const emptyData = {
+ dagRuns: [],
+ groups: {},
+};
+
const useTreeData = () => {
- const emptyData = {
- dagRuns: [],
- groups: {},
- };
const initialData = formatData(treeData, emptyData);
const { isRefreshOn, stopRefresh } = useAutoRefresh();
const errorToast = useErrorToast();
return useQuery('treeData', async () => {
try {
- const root = urlRoot ? `&root=${urlRoot}` : '';
- const base = baseDate ? `&base_date=${baseDate}` : '';
- const newData = await
axios.get(`${treeDataUrl}?dag_id=${dagId}&num_runs=${numRuns}${root}${base}`);
+ const params = new URLSearchParams({
+ dag_id: dagId,
+ });
+ if (numRuns && numRuns !== 25) params.append('num_runs', numRuns);
+ if (urlRoot) params.append('root', urlRoot);
+ if (baseDate) params.append('base_date', baseDate);
+
+ const newData = await axios.get(treeDataUrl, { params });
// turn off auto refresh if there are no active runs
if (!areActiveRuns(newData.dagRuns)) stopRefresh();
return newData;
@@ -62,6 +68,7 @@ const useTreeData = () => {
// only refetch if the refresh switch is on
refetchInterval: isRefreshOn && autoRefreshInterval * 1000,
initialData,
+ placeholderData: emptyData,
});
};
diff --git a/airflow/www/static/js/tree/api/useTreeData.test.jsx
b/airflow/www/static/js/tree/api/useTreeData.test.jsx
index 03d99ea7e0..01c59ef346 100644
--- a/airflow/www/static/js/tree/api/useTreeData.test.jsx
+++ b/airflow/www/static/js/tree/api/useTreeData.test.jsx
@@ -16,11 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React from 'react';
+
import { renderHook } from '@testing-library/react-hooks';
-import { QueryClient, QueryClientProvider } from 'react-query';
import useTreeData from './useTreeData';
-import { AutoRefreshProvider } from '../context/autorefresh';
+import { Wrapper } from '../utils/testUtils';
/* global describe, test, expect, beforeAll */
@@ -41,17 +40,6 @@ const pendingTreeData = {
],
};
-const Wrapper = ({ children }) => {
- const queryClient = new QueryClient();
- return (
- <AutoRefreshProvider>
- <QueryClientProvider client={queryClient}>
- {children}
- </QueryClientProvider>
- </AutoRefreshProvider>
- );
-};
-
describe('Test useTreeData hook', () => {
beforeAll(() => {
global.autoRefreshInterval = 5;
diff --git a/airflow/www/static/js/tree/dagRuns/index.jsx
b/airflow/www/static/js/tree/dagRuns/index.jsx
index c304b18bcf..462fffc8e3 100644
--- a/airflow/www/static/js/tree/dagRuns/index.jsx
+++ b/airflow/www/static/js/tree/dagRuns/index.jsx
@@ -38,7 +38,7 @@ const DurationTick = ({ children, ...rest }) => (
);
const DagRuns = () => {
- const { data: { dagRuns = [] } } = useTreeData();
+ const { data: { dagRuns } } = useTreeData();
const { selected, onSelect } = useSelection();
const durations = [];
const runs = dagRuns.map((dagRun) => {
diff --git a/airflow/www/static/js/tree/dagRuns/index.test.jsx
b/airflow/www/static/js/tree/dagRuns/index.test.jsx
index 15a30ce41a..550504b0fc 100644
--- a/airflow/www/static/js/tree/dagRuns/index.test.jsx
+++ b/airflow/www/static/js/tree/dagRuns/index.test.jsx
@@ -21,75 +21,43 @@
import React from 'react';
import { render } from '@testing-library/react';
-import { ChakraProvider, Table, Tbody } from '@chakra-ui/react';
import moment from 'moment-timezone';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { MemoryRouter } from 'react-router-dom';
import DagRuns from './index';
-import { ContainerRefProvider } from '../context/containerRef';
-import { TimezoneProvider } from '../context/timezone';
-import { AutoRefreshProvider } from '../context/autorefresh';
+import { TableWrapper } from '../utils/testUtils';
-global.moment = moment;
-
-const Wrapper = ({ children }) => {
- const queryClient = new QueryClient();
- return (
- <React.StrictMode>
- <ChakraProvider>
- <QueryClientProvider client={queryClient}>
- <ContainerRefProvider value={{}}>
- <TimezoneProvider value={{ timezone: 'UTC' }}>
- <AutoRefreshProvider value={{ isRefreshOn: false, stopRefresh:
() => {} }}>
- <MemoryRouter>
- <Table>
- <Tbody>
- {children}
- </Tbody>
- </Table>
- </MemoryRouter>
- </AutoRefreshProvider>
- </TimezoneProvider>
- </ContainerRefProvider>
- </QueryClientProvider>
- </ChakraProvider>
- </React.StrictMode>
- );
-};
+const dagRuns = [
+ {
+ dagId: 'dagId',
+ runId: 'run1',
+ dataIntervalStart: new Date(),
+ dataIntervalEnd: new Date(),
+ startDate: '2021-11-08T21:14:19.704433+00:00',
+ endDate: '2021-11-08T21:17:13.206426+00:00',
+ state: 'failed',
+ runType: 'scheduled',
+ executionDate: '2021-11-08T21:14:19.704433+00:00',
+ },
+ {
+ dagId: 'dagId',
+ runId: 'run2',
+ dataIntervalStart: new Date(),
+ dataIntervalEnd: new Date(),
+ state: 'success',
+ runType: 'manual',
+ startDate: '2021-11-09T00:19:43.023200+00:00',
+ endDate: '2021-11-09T00:22:18.607167+00:00',
+ },
+];
describe('Test DagRuns', () => {
- const dagRuns = [
- {
- dagId: 'dagId',
- runId: 'run1',
- dataIntervalStart: new Date(),
- dataIntervalEnd: new Date(),
- startDate: '2021-11-08T21:14:19.704433+00:00',
- endDate: '2021-11-08T21:17:13.206426+00:00',
- state: 'failed',
- runType: 'scheduled',
- executionDate: '2021-11-08T21:14:19.704433+00:00',
- },
- {
- dagId: 'dagId',
- runId: 'run2',
- dataIntervalStart: new Date(),
- dataIntervalEnd: new Date(),
- state: 'success',
- runType: 'manual',
- startDate: '2021-11-09T00:19:43.023200+00:00',
- endDate: '2021-11-09T00:22:18.607167+00:00',
- },
- ];
-
test('Durations and manual run arrow render correctly, but without any date
ticks', () => {
global.treeData = JSON.stringify({
groups: {},
dagRuns,
});
const { queryAllByTestId, getByText, queryByText } = render(
- <DagRuns />, { wrapper: Wrapper },
+ <DagRuns />, { wrapper: TableWrapper },
);
expect(queryAllByTestId('run')).toHaveLength(2);
expect(queryAllByTestId('manual-run')).toHaveLength(1);
@@ -127,26 +95,15 @@ describe('Test DagRuns', () => {
],
});
const { getByText } = render(
- <DagRuns />, { wrapper: Wrapper },
+ <DagRuns />, { wrapper: TableWrapper },
);
expect(getByText(moment.utc(dagRuns[0].executionDate).format('MMM DD,
HH:mm'))).toBeInTheDocument();
});
test('Handles empty data correctly', () => {
- global.treeData = {
- groups: {},
- dagRuns: [],
- };
- const { queryByTestId } = render(
- <DagRuns />, { wrapper: Wrapper },
- );
- expect(queryByTestId('run')).toBeNull();
- });
-
- test('Handles no data correctly', () => {
- global.treeData = {};
+ global.treeData = null;
const { queryByTestId } = render(
- <DagRuns />, { wrapper: Wrapper },
+ <DagRuns />, { wrapper: TableWrapper },
);
expect(queryByTestId('run')).toBeNull();
});
diff --git a/airflow/www/static/js/tree/details/Header.jsx
b/airflow/www/static/js/tree/details/Header.jsx
index 5ce3b0583d..2bdaa47361 100644
--- a/airflow/www/static/js/tree/details/Header.jsx
+++ b/airflow/www/static/js/tree/details/Header.jsx
@@ -43,7 +43,7 @@ const LabelValue = ({ label, value }) => (
);
const Header = () => {
- const { data: { dagRuns = [] } } = useTreeData();
+ const { data: { dagRuns } } = useTreeData();
const { selected: { taskId, runId }, onSelect, clearSelection } =
useSelection();
const { data: { tasks } } = useTasks();
const dagRun = dagRuns.find((r) => r.runId === runId);
diff --git a/airflow/www/static/js/tree/details/content/Dag.jsx
b/airflow/www/static/js/tree/details/content/Dag.jsx
index 928b4f3f55..511844d72e 100644
--- a/airflow/www/static/js/tree/details/content/Dag.jsx
+++ b/airflow/www/static/js/tree/details/content/Dag.jsx
@@ -42,7 +42,7 @@ const dagDetailsUrl = getMetaValue('dag_details_url');
const Dag = () => {
const { data: taskData } = useTasks(dagId);
- const { data: { dagRuns = [] } } = useTreeData();
+ const { data: { dagRuns } } = useTreeData();
if (!taskData) return null;
const { tasks = [], totalEntries = '' } = taskData;
diff --git a/airflow/www/static/js/tree/details/content/dagRun/index.jsx
b/airflow/www/static/js/tree/details/content/dagRun/index.jsx
index 1816385c58..bd096f1510 100644
--- a/airflow/www/static/js/tree/details/content/dagRun/index.jsx
+++ b/airflow/www/static/js/tree/details/content/dagRun/index.jsx
@@ -43,7 +43,7 @@ const graphUrl = getMetaValue('graph_url');
const dagRunDetailsUrl = getMetaValue('dagrun_details_url');
const DagRun = ({ runId }) => {
- const { data: { dagRuns = [] } } = useTreeData();
+ const { data: { dagRuns } } = useTreeData();
const run = dagRuns.find((dr) => dr.runId === runId);
if (!run) return null;
const {
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
index 0e4f441e24..e40471f071 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
@@ -56,7 +56,7 @@ const getTask = ({ taskId, runId, task }) => {
const TaskInstance = ({ taskId, runId }) => {
const [selectedRows, setSelectedRows] = useState([]);
- const { data: { groups = {}, dagRuns = [] } } = useTreeData();
+ const { data: { groups, dagRuns } } = useTreeData();
const group = getTask({ taskId, runId, task: groups });
const run = dagRuns.find((r) => r.runId === runId);
const { executionDate } = run;
diff --git a/airflow/www/static/js/tree/renderTaskRows.test.jsx
b/airflow/www/static/js/tree/renderTaskRows.test.jsx
index 88afb4a580..415f18b89d 100644
--- a/airflow/www/static/js/tree/renderTaskRows.test.jsx
+++ b/airflow/www/static/js/tree/renderTaskRows.test.jsx
@@ -21,15 +21,9 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
-import { ChakraProvider, Table, Tbody } from '@chakra-ui/react';
-import moment from 'moment';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { MemoryRouter } from 'react-router-dom';
import renderTaskRows from './renderTaskRows';
-import { ContainerRefProvider } from './context/containerRef';
-
-global.moment = moment;
+import { TableWrapper } from './utils/testUtils';
const mockTreeData = {
groups: {
@@ -94,27 +88,6 @@ const mockTreeData = {
],
};
-const Wrapper = ({ children }) => {
- const queryClient = new QueryClient();
- return (
- <React.StrictMode>
- <ChakraProvider>
- <QueryClientProvider client={queryClient}>
- <ContainerRefProvider value={{}}>
- <MemoryRouter>
- <Table>
- <Tbody>
- {children}
- </Tbody>
- </Table>
- </MemoryRouter>
- </ContainerRefProvider>
- </QueryClientProvider>
- </ChakraProvider>
- </React.StrictMode>
- );
-};
-
describe('Test renderTaskRows', () => {
test('Group defaults to closed but clicking on the name will open a group',
() => {
global.treeData = mockTreeData;
@@ -123,7 +96,7 @@ describe('Test renderTaskRows', () => {
const { getByTestId, getByText, getAllByTestId } = render(
<>{renderTaskRows({ task, dagRunIds })}</>,
- { wrapper: Wrapper },
+ { wrapper: TableWrapper },
);
const groupName = getByText('group_1');
@@ -168,7 +141,7 @@ describe('Test renderTaskRows', () => {
const { queryByTestId, getByText } = render(
<>{renderTaskRows({ task, dagRunIds: [] })}</>,
- { wrapper: Wrapper },
+ { wrapper: TableWrapper },
);
expect(getByText('group_1')).toBeInTheDocument();
diff --git a/airflow/www/static/js/tree/utils/testUtils.jsx
b/airflow/www/static/js/tree/utils/testUtils.jsx
new file mode 100644
index 0000000000..946fe0dd88
--- /dev/null
+++ b/airflow/www/static/js/tree/utils/testUtils.jsx
@@ -0,0 +1,67 @@
+/*!
+ * 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 React from 'react';
+import { ChakraProvider, Table, Tbody } from '@chakra-ui/react';
+import { QueryClient, QueryClientProvider } from 'react-query';
+import { MemoryRouter } from 'react-router-dom';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import moment from 'moment-timezone';
+
+import { ContainerRefProvider } from '../context/containerRef';
+import { TimezoneProvider } from '../context/timezone';
+import { AutoRefreshProvider } from '../context/autorefresh';
+
+global.moment = moment;
+
+export const Wrapper = ({ children }) => {
+ const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ cacheTime: Infinity,
+ staleTime: Infinity,
+ },
+ },
+ });
+ return (
+ <ChakraProvider>
+ <QueryClientProvider client={queryClient}>
+ <ContainerRefProvider>
+ <TimezoneProvider>
+ <AutoRefreshProvider>
+ <MemoryRouter>
+ {children}
+ </MemoryRouter>
+ </AutoRefreshProvider>
+ </TimezoneProvider>
+ </ContainerRefProvider>
+ </QueryClientProvider>
+ </ChakraProvider>
+ );
+};
+
+export const TableWrapper = ({ children }) => (
+ <Wrapper>
+ <Table>
+ <Tbody>
+ {children}
+ </Tbody>
+ </Table>
+ </Wrapper>
+);