From 51257a2f62fea64242115e5948b2752edd280a53 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Fri, 26 Mar 2021 12:22:02 -0400 Subject: [PATCH 1/3] Adds observability metrics chart wip tooltip renders with colors and disappears scales y axis properly adds legend without buttlets adds legend data but needs styling adds legend, and cleans up code show help text --- awx/ui_next/src/App.jsx | 43 +++ awx/ui_next/src/api/index.js | 3 + awx/ui_next/src/api/models/Metrics.js | 2 +- .../ObservabilityMetrics/LineChart.jsx | 272 ++++++++++++++++++ .../ObservabilityMetrics/LineChart.test.jsx | 37 +++ .../ObservabilityMetrics.jsx | 239 +++++++++++++++ .../ObservabilityMetrics.test.jsx | 72 +++++ .../src/screens/ObservabilityMetrics/index.js | 1 + 8 files changed, 668 insertions(+), 1 deletion(-) create mode 100644 awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx create mode 100644 awx/ui_next/src/screens/ObservabilityMetrics/LineChart.test.jsx create mode 100644 awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx create mode 100644 awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx create mode 100644 awx/ui_next/src/screens/ObservabilityMetrics/index.js diff --git a/awx/ui_next/src/App.jsx b/awx/ui_next/src/App.jsx index 2c1c1abd57..69d5dd2417 100644 --- a/awx/ui_next/src/App.jsx +++ b/awx/ui_next/src/App.jsx @@ -20,6 +20,7 @@ import Login from './screens/Login'; import { isAuthenticated } from './util/auth'; import { getLanguageWithoutRegionCode } from './util/language'; import { dynamicActivate, locales } from './i18nLoader'; +import ObservabilityMetrics from './screens/ObservabilityMetrics'; import getRouteConfig from './routeConfig'; import SubscriptionEdit from './screens/Setting/Subscription/SubscriptionEdit'; @@ -87,6 +88,7 @@ function App() { const { hash, search, pathname } = useLocation(); return ( +<<<<<<< HEAD @@ -108,6 +110,47 @@ function App() { +======= + + + {({ i18n }) => ( + + + + + + + + + + + + + + + {getRouteConfig(i18n) + .flatMap(({ routes }) => routes) + .map(({ path, screen: Screen }) => ( + + + + )) + .concat( + + + , + + + + )} + + + + + + )} + +>>>>>>> Adds observability metrics chart ); } diff --git a/awx/ui_next/src/api/index.js b/awx/ui_next/src/api/index.js index d048237b74..ff2d0e6fba 100644 --- a/awx/ui_next/src/api/index.js +++ b/awx/ui_next/src/api/index.js @@ -20,6 +20,7 @@ import JobTemplates from './models/JobTemplates'; import Jobs from './models/Jobs'; import Labels from './models/Labels'; import Me from './models/Me'; +import Metrics from './models/Metrics'; import NotificationTemplates from './models/NotificationTemplates'; import Notifications from './models/Notifications'; import Organizations from './models/Organizations'; @@ -64,6 +65,7 @@ const JobTemplatesAPI = new JobTemplates(); const JobsAPI = new Jobs(); const LabelsAPI = new Labels(); const MeAPI = new Me(); +const MetricsAPI = new Metrics(); const NotificationTemplatesAPI = new NotificationTemplates(); const NotificationsAPI = new Notifications(); const OrganizationsAPI = new Organizations(); @@ -109,6 +111,7 @@ export { JobsAPI, LabelsAPI, MeAPI, + MetricsAPI, NotificationTemplatesAPI, NotificationsAPI, OrganizationsAPI, diff --git a/awx/ui_next/src/api/models/Metrics.js b/awx/ui_next/src/api/models/Metrics.js index e808d26662..f4451aefd9 100644 --- a/awx/ui_next/src/api/models/Metrics.js +++ b/awx/ui_next/src/api/models/Metrics.js @@ -3,7 +3,7 @@ import Base from '../Base'; class Metrics extends Base { constructor(http) { super(http); - this.baseUrl = '/api/v2/inventories/'; + this.baseUrl = '/api/v2/metrics/'; } } export default Metrics; diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx new file mode 100644 index 0000000000..2f02b8a5aa --- /dev/null +++ b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx @@ -0,0 +1,272 @@ +import React, { useEffect, useCallback } from 'react'; +import * as d3 from 'd3'; +import { t } from '@lingui/macro'; +import { withI18n } from '@lingui/react'; +import { PageContextConsumer } from '@patternfly/react-core'; + +function LineChart({ data, i18n, helpText }) { + console.log(data, 'data'); + const count = data[0]?.values.length; + const draw = useCallback(() => { + const margin = 80; + const getWidth = () => { + let width; + // This is in an a try/catch due to an error from jest. + // Even though the d3.select returns a valid selector with + // style function, it says it is null in the test + try { + width = + parseInt(d3.select(`#chart`).style('width'), 10) - margin || 700; + } catch (error) { + width = 700; + } + + return width; + }; + const width = getWidth(); + const height = 500; + const duration = 250; + const circleRadius = 6; + const circleRadiusHover = 8; + + /* Scale */ + let smallestY; + data.map(line => + line.values.forEach(value => { + if (smallestY === undefined) { + smallestY = value.y; + } + if (value.y < smallestY) { + smallestY = value.y; + } + }) + ); + + const xScale = d3 + .scaleLinear() + .domain(d3.extent(data[0].values, d => d.x)) + .range([0, width - margin]); + + const yScale = d3 + .scaleLinear() + .domain([smallestY, d3.max(data[0].values, d => d.y)]) + .range([height - margin, 0]); + + const color = d3.scaleOrdinal(d3.schemeCategory10); + + /* Add SVG */ + d3.selectAll(`#chart > *`).remove(); + + const renderTooltip = d => { + d3.selectAll(`.tooltip > *`).remove(); + + d3.select('#chart') + .append('span') + .attr('class', 'tooltip') + .attr('stroke', 'black') + .attr('fill', 'white') + .style('padding-left', '50px'); + const tooltip = {}; + data.map(datum => { + datum.values.forEach(value => { + if (d.x === value.x) { + tooltip[datum.name] = value.y; + } + }); + return tooltip; + }); + Object.entries(tooltip).forEach(([key, value], i) => { + d3.select('.tooltip') + .append('span') + .attr('class', 'tooltip-text-wrapper') + .append('text') + .attr('class', 'tooltip-text') + .style('color', color(i)) + .style('padding-right', '20px') + .text(`${key}: ${value}`); + }); + }; + const removeTooltip = () => { + d3.select('.tooltip') + .style('cursor', 'none') + .selectAll(`.tooltip > *`) + .remove(); + }; + + // Add legend + d3.selectAll(`.legend > *`).remove(); + const legendContainer = d3 + .select('#chart') + .append('div') + .style('display', 'flex') + .attr('class', 'legend') + .attr('height', '400px') + .attr('width', '500px') + .style('padding-left', '50px'); + + legendContainer + .append('text') + .attr('class', 'legend-title') + .attr('x', '100') + .attr('y', '50') + .text('Legend'); + + legendContainer.data(data, (d, i) => { + if (d?.name) { + const legendItemContainer = legendContainer + .append('div') + .style('display', 'flex') + .attr('id', 'legend-item-container') + .style('padding-left', '20px'); + + legendItemContainer + .append('div') + .style('background-color', color(i)) + .style('height', '8px') + .style('width', '8px') + .style('border-radius', '50%') + .style('padding', '5px') + .style('margin-top', '6px'); + + legendItemContainer + .append('text') + .style('padding-left', '20px') + .text(d.name); + } + }); + + // Add help text to top of chart + + d3.select('#chart') + .append('div') + .attr('class', 'help-text') + .style('padding-left', '50px') + .style('padding-top', '20px') + .text(helpText); + + const svg = d3 + .select('#chart') + .append('svg') + .attr('width', `${width + margin}px`) + .attr('height', `${height + margin}px`) + .append('g') + .attr('transform', `translate(${margin}, ${margin})`); + + /* Add line into SVG */ + const line = d3 + .line() + .curve(d3.curveMonotoneX) + .x(d => xScale(d.x)) + .y(d => yScale(d.y)); + + const lines = svg.append('g'); + + lines + .selectAll('.line-group') + .data(data) + .enter() + .append('g') + .attr('class', 'line-group') + .append('path') + .attr('class', 'line') + .style('fill', 'none') + .attr('d', d => line(d.values)) + .style('stroke', (d, i) => color(i)) + .style('stroke-width', '3px'); + + /* Add circles in the line */ + lines + .selectAll('circle-group') + .data(data) + .enter() + .append('g') + .style('fill', (d, i) => color(i)) + .selectAll('circle') + .data(d => d.values) + .enter() + .append('g') + .attr('class', 'circle') + .on('mouseover', (d, i) => { + if (data.length) { + renderTooltip(d, i); + } + }) + .on('mouseout', () => { + removeTooltip(); + }) + .append('circle') + .attr('cx', d => xScale(d.x)) + .attr('cy', d => yScale(d.y)) + .attr('r', circleRadius) + .on('mouseover', () => { + d3.select(this) + .transition() + .duration(duration) + .attr('r', circleRadiusHover); + }) + .on('mouseout', () => { + d3.select(this) + .transition() + .duration(duration) + .attr('r', circleRadius); + }); + + /* Add Axis into SVG */ + const xAxis = d3 + .axisBottom(xScale) + .ticks(data[0].values.length > 5 ? data[0].values.length : 5); + const yAxis = d3.axisLeft(yScale).ticks(5); + + svg + .append('g') + .attr('class', 'x axis') + .attr('transform', `translate(0, ${height - margin})`) + .call(xAxis) + .append('text') + .attr('x', 30) + .attr('y', 30) + .attr('fill', '#000') + .text(i18n._(t`Count`)); + + svg + .append('g') + .attr('class', 'y axis') + .call(yAxis) + .append('text') + .attr('y', -30) + .attr('x', 0) + .attr('transform', 'rotate(-90)') + .attr('fill', '#000') + .text(i18n._(t`Values`)); + }, [data, i18n, helpText]); + + useEffect(() => { + draw(); + }, [count, draw]); + + useEffect(() => { + function handleResize() { + draw(); + } + + window.addEventListener('resize', handleResize); + + handleResize(); + + return () => window.removeEventListener('resize', handleResize); + }, [draw]); + + return
; +} + +const withPageContext = Component => { + return function contextComponent(props) { + return ( + + {pageContext => } + + ); + }; +}; + +export default withI18n()(withPageContext(LineChart)); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.test.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.test.jsx new file mode 100644 index 0000000000..a19e20072f --- /dev/null +++ b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.test.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; + +import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; +import LineChart from './LineChart'; + +describe('', () => { + test('should render properly', async () => { + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + expect(wrapper.find('LineChart').length).toBe(1); + }); +}); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx new file mode 100644 index 0000000000..24dae3168d --- /dev/null +++ b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx @@ -0,0 +1,239 @@ +import React, { useEffect, useCallback, useState, useRef } from 'react'; +import { withI18n } from '@lingui/react'; +import { t } from '@lingui/macro'; +import { + PageSection, + Card, + CardHeader, + CardBody, + Toolbar, + ToolbarGroup, + ToolbarContent, + ToolbarItem, + Select, + SelectOption, +} from '@patternfly/react-core'; + +import LineChart from './LineChart'; +import { MetricsAPI, InstancesAPI } from '../../api'; +import useRequest from '../../util/useRequest'; +import ContentEmpty from '../../components/ContentEmpty'; +import ContentError from '../../components/ContentError'; + +let count = [0]; + +// hook thats calls api every 3 seconds to get data +function useInterval(callback, delay, instance, metric) { + const savedCallback = useRef(); + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + useEffect(() => { + function tick() { + count.push(count.length); + if (instance && metric) { + savedCallback.current(); + } + } + + const id = setInterval(tick, delay); + return () => { + clearInterval(id); + }; + }, [callback, delay, instance, metric]); + return { count }; +} +function ObservabilityMetrics({ i18n }) { + const [instanceIsOpen, setInstanceIsOpen] = useState(false); + const [instance, setInstance] = useState(null); + const [metric, setMetric] = useState(null); + const [metricIsOpen, setMetricIsOpen] = useState(false); + const [renderedData, setRenderedData] = useState([]); + const { + result: { instances, metrics }, + error: fetchInitialError, + request: fetchInstances, + } = useRequest( + useCallback(async () => { + const [ + { + data: { results }, + }, + { data: mets }, + ] = await Promise.all([ + InstancesAPI.read(), + MetricsAPI.read({ + subsystemonly: 1, + format: 'json', + }), + ]); + + const metricOptions = Object.keys(mets); + + return { + instances: [...results.map(result => result.hostname), 'All'], + metrics: metricOptions, + }; + }, []), + { instances: [], metrics: [] } + ); + + const { + result: helpText, + error: updateError, + request: fetchData, + } = useRequest( + useCallback(async () => { + const { data } = await MetricsAPI.read({ + subsystemonly: 1, + format: 'json', + node: instance === 'All' ? null : instance, + metric, + }); + + const rendered = renderedData; + const instanceData = Object.values(data); + instanceData.forEach(value => { + value.samples.forEach(sample => { + instances.forEach(i => { + if (i === sample.labels.node) { + const renderedIndex = renderedData.findIndex(rd => rd.name === i); + + if (renderedIndex === -1) { + rendered.push({ + name: i, + values: [ + { + y: sample.value, + x: count.length - 1, + }, + ], + }); + } else if ( + rendered[renderedIndex].values?.length === 0 || + !rendered[renderedIndex].values + ) { + rendered[renderedIndex].values = [ + { y: sample.value, x: count.length - 1 }, + ]; + } else { + rendered[renderedIndex].values = [ + ...rendered[renderedIndex].values, + { y: sample.value, x: count.length - 1 }, + ]; + } + } + }); + }); + }); + let countRestrictedData = rendered; + if (count.length > 49) { + countRestrictedData = rendered.map(({ values, name }) => ({ + name, + values: values.slice(-50), + })); + } + + setRenderedData(countRestrictedData); + return data[metric].help_text; + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [instance, metric, instances]), + '' + ); + + useInterval(fetchData, 3000, instance, metric); + + useEffect(() => { + if (instance && metric) { + fetchData(); + } + }, [fetchData, instance, metric]); + + useEffect(() => { + fetchInstances(); + }, [fetchInstances]); + if (fetchInitialError || updateError) { + console.log('here'); + return ( + + + + ; + + + + ); + } + return ( + + + + + + + {i18n._(t`Instance`)} + + + + {i18n._(t`Metric`)} + + + + + + + + + {instance && metric ? ( + Object.keys(renderedData).length > 0 && ( + + ) + ) : ( + + )} + + + + ); +} + +export default withI18n()(ObservabilityMetrics); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx new file mode 100644 index 0000000000..4fcf8a86a5 --- /dev/null +++ b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; + +import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; +import ObservabilityMetrics from './ObservabilityMetrics'; +import { MetricsAPI, InstancesAPI } from '../../api'; + +jest.mock('../../api/models/Instances'); +jest.mock('../../api/models/Metrics'); +InstancesAPI.read.mockResolvedValue({ + data: { results: [{ hostname: 'instance 1' }, { hostname: 'instance 2' }] }, +}); +MetricsAPI.read.mockResolvedValue({ + data: { + metric1: { + helptext: 'metric 1 help text', + samples: [{ labels: { node: 'metric 1' }, value: 20 }], + }, + metric2: { + helptext: 'metric 2 help text', + samples: [{ labels: { node: 'metric 2' }, value: 10 }], + }, + }, +}); + +describe('', () => { + let wrapper; + beforeEach(async () => { + await act(async () => { + wrapper = mountWithContexts(); + }); + }); + afterEach(() => { + wrapper.unmount(); + jest.clearAllMocks(); + }); + test('should mound properly', () => { + expect(wrapper.find('ObservabilityMetrics').length).toBe(1); + expect(wrapper.find('EmptyStateBody').length).toBe(1); + expect(wrapper.find('ChartLine').length).toBe(0); + }); + test('should render chart after selecting metric and instance', async () => { + await act(async () => { + wrapper.find('Select[ouiaId="Instance-select"]').prop('onToggle')(true); + }); + wrapper.update(); + await act(async () => { + wrapper + .find('SelectOption[value="instance 1"]') + .find('button') + .prop('onClick')({}, 'instance 1'); + }); + wrapper.update(); + await act(() => { + wrapper.find('Select[ouiaId="Metric-select"]').prop('onToggle')(true); + }); + wrapper.update(); + await act(async () => { + wrapper + .find('SelectOption[value="metric1"]') + .find('button') + .prop('onClick')({}, 'metric1'); + }); + wrapper.update(); + expect(MetricsAPI.read).toBeCalledWith({ + subsystemonly: 1, + format: 'json', + metric: 'metric1', + node: 'instance 1', + }); + }); +}); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/index.js b/awx/ui_next/src/screens/ObservabilityMetrics/index.js new file mode 100644 index 0000000000..e1ebe1cb96 --- /dev/null +++ b/awx/ui_next/src/screens/ObservabilityMetrics/index.js @@ -0,0 +1 @@ +export { default } from './ObservabilityMetrics'; From b933155f0793ce4b9fffec4c79eed1d28e077c56 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Wed, 7 Apr 2021 11:17:40 -0400 Subject: [PATCH 2/3] fixes some domain issues for x and y axis --- .../ObservabilityMetrics/LineChart.jsx | 39 +++++++++---------- .../ObservabilityMetrics.jsx | 4 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx index 2f02b8a5aa..48872214b3 100644 --- a/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx +++ b/awx/ui_next/src/screens/ObservabilityMetrics/LineChart.jsx @@ -1,11 +1,8 @@ import React, { useEffect, useCallback } from 'react'; import * as d3 from 'd3'; -import { t } from '@lingui/macro'; -import { withI18n } from '@lingui/react'; import { PageContextConsumer } from '@patternfly/react-core'; -function LineChart({ data, i18n, helpText }) { - console.log(data, 'data'); +function LineChart({ data, helpText }) { const count = data[0]?.values.length; const draw = useCallback(() => { const margin = 80; @@ -31,6 +28,7 @@ function LineChart({ data, i18n, helpText }) { /* Scale */ let smallestY; + let largestY; data.map(line => line.values.forEach(value => { if (smallestY === undefined) { @@ -39,17 +37,27 @@ function LineChart({ data, i18n, helpText }) { if (value.y < smallestY) { smallestY = value.y; } + if (largestY === undefined) { + largestY = smallestY + 10; + } + if (value.y > largestY) { + largestY = value.y; + } }) ); const xScale = d3 .scaleLinear() - .domain(d3.extent(data[0].values, d => d.x)) + .domain( + d3.max(data[0].values, d => d.x) > 49 + ? d3.extent(data[0].values, d => d.x) + : [0, 50] + ) .range([0, width - margin]); const yScale = d3 .scaleLinear() - .domain([smallestY, d3.max(data[0].values, d => d.y)]) + .domain([smallestY, largestY]) .range([height - margin, 0]); const color = d3.scaleOrdinal(d3.schemeCategory10); @@ -221,24 +229,13 @@ function LineChart({ data, i18n, helpText }) { .append('g') .attr('class', 'x axis') .attr('transform', `translate(0, ${height - margin})`) - .call(xAxis) - .append('text') - .attr('x', 30) - .attr('y', 30) - .attr('fill', '#000') - .text(i18n._(t`Count`)); + .call(xAxis); svg .append('g') .attr('class', 'y axis') - .call(yAxis) - .append('text') - .attr('y', -30) - .attr('x', 0) - .attr('transform', 'rotate(-90)') - .attr('fill', '#000') - .text(i18n._(t`Values`)); - }, [data, i18n, helpText]); + .call(yAxis); + }, [data, helpText]); useEffect(() => { draw(); @@ -269,4 +266,4 @@ const withPageContext = Component => { }; }; -export default withI18n()(withPageContext(LineChart)); +export default withPageContext(LineChart); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx index 24dae3168d..0b08f00036 100644 --- a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx +++ b/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.jsx @@ -185,7 +185,7 @@ function ObservabilityMetrics({ i18n }) { setRenderedData([]); }} selections={instance} - placeholderText={i18n._(t`Select an instance`)} + placeholderText={i18n._(t`Select a instance`)} > {instances.map(inst => ( @@ -196,7 +196,7 @@ function ObservabilityMetrics({ i18n }) { { - count = [0]; - setInstance(value); - setInstanceIsOpen(false); - setRenderedData([]); - }} - selections={instance} - placeholderText={i18n._(t`Select a instance`)} - > - {instances.map(inst => ( - - ))} - - - {i18n._(t`Metric`)} - - - - - - - - - {instance && metric ? ( - Object.keys(renderedData).length > 0 && ( - + + + + + + + + + {i18n._(t`Instance`)} + + + + {i18n._(t`Metric`)} + + + + + + + + + {instance && metric ? ( + Object.keys(renderedData).length > 0 && ( + + ) + ) : ( + - ) - ) : ( - - )} - - - + )} + + + + ); } -export default withI18n()(ObservabilityMetrics); +export default withI18n()(Metrics); diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx b/awx/ui_next/src/screens/Metrics/Metrics.test.jsx similarity index 89% rename from awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx rename to awx/ui_next/src/screens/Metrics/Metrics.test.jsx index 4fcf8a86a5..fb74d6d929 100644 --- a/awx/ui_next/src/screens/ObservabilityMetrics/ObservabilityMetrics.test.jsx +++ b/awx/ui_next/src/screens/Metrics/Metrics.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; -import ObservabilityMetrics from './ObservabilityMetrics'; +import Metrics from './Metrics'; import { MetricsAPI, InstancesAPI } from '../../api'; jest.mock('../../api/models/Instances'); @@ -23,11 +23,11 @@ MetricsAPI.read.mockResolvedValue({ }, }); -describe('', () => { +describe('', () => { let wrapper; beforeEach(async () => { await act(async () => { - wrapper = mountWithContexts(); + wrapper = mountWithContexts(); }); }); afterEach(() => { @@ -35,7 +35,7 @@ describe('', () => { jest.clearAllMocks(); }); test('should mound properly', () => { - expect(wrapper.find('ObservabilityMetrics').length).toBe(1); + expect(wrapper.find('Metrics').length).toBe(1); expect(wrapper.find('EmptyStateBody').length).toBe(1); expect(wrapper.find('ChartLine').length).toBe(0); }); diff --git a/awx/ui_next/src/screens/Metrics/index.js b/awx/ui_next/src/screens/Metrics/index.js new file mode 100644 index 0000000000..8e5880233f --- /dev/null +++ b/awx/ui_next/src/screens/Metrics/index.js @@ -0,0 +1 @@ +export { default } from './Metrics'; diff --git a/awx/ui_next/src/screens/ObservabilityMetrics/index.js b/awx/ui_next/src/screens/ObservabilityMetrics/index.js deleted file mode 100644 index e1ebe1cb96..0000000000 --- a/awx/ui_next/src/screens/ObservabilityMetrics/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ObservabilityMetrics';