From e44c0b3811e1df2bdd2c0d0755b713262876c5b9 Mon Sep 17 00:00:00 2001 From: Isaiah Thiessen Date: Thu, 4 Aug 2022 10:28:43 -0700 Subject: [PATCH] feat: unit tests for Synthetics Card, update ProblemsList tests Signed-off-by: Isaiah Thiessen --- .changeset/fresh-rabbits-juggle.md | 9 ++++ plugins/dynatrace/README.md | 19 +++++++ .../components/EmptyState/EmptyState.test.tsx | 15 ------ .../src/components/EmptyState/EmptyState.tsx | 7 ++- .../ProblemsList/ProblemsList.test.tsx | 14 ++++-- .../Problems/ProblemsList/ProblemsList.tsx | 20 +++++--- .../ProblemsTable/ProblemsTable.test.tsx | 12 +---- .../SyntheticsCard/SyntheticsCard.test.tsx | 40 ++++++++++++++- .../SyntheticsLocation.test.tsx | 49 ++++++++++++++++++- .../SyntheticsLocation/SyntheticsLocation.tsx | 24 ++++----- plugins/dynatrace/src/mocks/problems.json | 2 +- 11 files changed, 155 insertions(+), 56 deletions(-) create mode 100644 .changeset/fresh-rabbits-juggle.md delete mode 100644 plugins/dynatrace/src/components/EmptyState/EmptyState.test.tsx diff --git a/.changeset/fresh-rabbits-juggle.md b/.changeset/fresh-rabbits-juggle.md new file mode 100644 index 0000000000..804ae70853 --- /dev/null +++ b/.changeset/fresh-rabbits-juggle.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-dynatrace': minor +--- + +New features: + +- Some visual improvements to the table that displays Problems +- Added support for viewing recent Synthetics results using +- Added some additional linking to the configured Dynatrace instance diff --git a/plugins/dynatrace/README.md b/plugins/dynatrace/README.md index 150c9768af..2f5d30b88a 100644 --- a/plugins/dynatrace/README.md +++ b/plugins/dynatrace/README.md @@ -38,6 +38,8 @@ dynatrace: #### Catalog Configuration +##### View Recent Application Problems + To show information from Dynatrace for a catalog entity, add the following annotation to `catalog-info.yaml`: ```yaml @@ -51,6 +53,23 @@ metadata: The `DYNATRACE_ENTITY_ID` can be found in Dynatrace by browsing to the entity (a service, synthetic, frontend, workload, etc.). It will be located in the browser address bar in the `id` parameter and has the format `ENTITY_TYPE-ENTITY_ID`, where `ENTITY_TYPE` will be one of `SERVICE`, `SYNTHETIC_TEST`, or other, and `ENTITY_ID` will be a string of characters containing uppercase letters and numbers. +##### Viewing Recent Synthetics Results + +To show recent results from a Synthetic Monitor, add the following annotation to `catalog-info.yaml`: + +```yaml +# catalog-info.yaml +# [...] +metadata: + annotations: + dynatrace.com/dynatrace-synthetics-ids: SYNTHETIC_ID, SYNTHETIC_ID_2, ... +# [...] +``` + +The annotation can also contain a comma separated list of Synthetic Ids to surface details for multiple monitors! + +The `SYNTHETIC_ID` can be found in Dynatrace by browsing to the Synthetic monitor. It will be located in the browser address bar in the resource path - `https://example.dynatrace.com/ui/http-monitor/HTTP_CHECK-1234` for an Http check, or `https://example.dynatrace.com/ui/browser-monitor/SYNTHETIC_TEST-1234` for a browser clickpath. + ## Disclaimer This plugin is not officially supported by Dynatrace. diff --git a/plugins/dynatrace/src/components/EmptyState/EmptyState.test.tsx b/plugins/dynatrace/src/components/EmptyState/EmptyState.test.tsx deleted file mode 100644 index b61d59e88d..0000000000 --- a/plugins/dynatrace/src/components/EmptyState/EmptyState.test.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed 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. - */ diff --git a/plugins/dynatrace/src/components/EmptyState/EmptyState.tsx b/plugins/dynatrace/src/components/EmptyState/EmptyState.tsx index ab6eecdb7e..87d0099499 100644 --- a/plugins/dynatrace/src/components/EmptyState/EmptyState.tsx +++ b/plugins/dynatrace/src/components/EmptyState/EmptyState.tsx @@ -17,7 +17,12 @@ import React from 'react'; import { Grid, Typography } from '@material-ui/core'; import EmptyStateImage from '../../assets/emptystate.svg'; -export const EmptyState = ({ message }) => { +type EmptyStateProps = { + message: string; +}; + +export const EmptyState = (props: EmptyStateProps) => { + const { message } = props; return ( { .mockResolvedValue({ problems }); const rendered = await renderInTestApp( - + , ); expect(await rendered.findByText('example-service')).toBeInTheDocument(); }); - it('renders "nothing to report :)" if no problems are found', async () => { + it('returns "No Problems to Report!" if no problems are found', async () => { mockDynatraceApi.getDynatraceProblems = jest.fn().mockResolvedValue({}); const rendered = await renderInTestApp( - + , ); expect( - await rendered.findByText('Nothing to report :)'), + await rendered.findByText('No Problems to Report!'), ).toBeInTheDocument(); }); }); diff --git a/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx index 6a6ade9520..17ab1842e9 100644 --- a/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx +++ b/plugins/dynatrace/src/components/Problems/ProblemsList/ProblemsList.tsx @@ -28,6 +28,17 @@ type ProblemsListProps = { dynatraceBaseUrl: string; }; +const cardContents = (problems: Array, dynatraceBaseUrl: string) => { + return problems?.length ? ( + + ) : ( + + ); +}; + export const ProblemsList = (props: ProblemsListProps) => { const { dynatraceEntityId, dynatraceBaseUrl } = props; const dynatraceApi = useApi(dynatraceApiRef); @@ -50,14 +61,7 @@ export const ProblemsList = (props: ProblemsListProps) => { link: `${dynatraceBaseUrl}/#serviceOverview;id=${dynatraceEntityId}`, }} > - {value?.totalCount ? ( - - ) : ( - - )} + {cardContents(problems, dynatraceBaseUrl)} ); }; diff --git a/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx index e4adc43656..aaa3ea0024 100644 --- a/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx +++ b/plugins/dynatrace/src/components/Problems/ProblemsTable/ProblemsTable.test.tsx @@ -28,17 +28,9 @@ describe('ProblemsTable', () => { it('renders the table with some problem data', async () => { const rendered = await renderInTestApp( - , + , , ); - expect(await rendered.findByText('example-service')).toBeInTheDocument(); - }); - it('renders an empty table when no data is provided', async () => { - const rendered = await renderInTestApp( - - - , - ); - expect(await rendered.findByText('Problems')).toBeInTheDocument(); + expect(await rendered.findByTitle('Search')).toBeInTheDocument(); }); }); diff --git a/plugins/dynatrace/src/components/Synthetics/SyntheticsCard/SyntheticsCard.test.tsx b/plugins/dynatrace/src/components/Synthetics/SyntheticsCard/SyntheticsCard.test.tsx index 89f10c5750..8a06a140b4 100644 --- a/plugins/dynatrace/src/components/Synthetics/SyntheticsCard/SyntheticsCard.test.tsx +++ b/plugins/dynatrace/src/components/Synthetics/SyntheticsCard/SyntheticsCard.test.tsx @@ -14,5 +14,43 @@ * limitations under the License. */ import React from 'react'; +import { SyntheticsCard } from './SyntheticsCard'; +import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { dynatraceApiRef } from '../../../api'; +import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; -describe('SyntheticsCard', () => {}); +const mockDynatraceApi = { + getDynatraceSyntheticFailures: jest.fn(), +}; +const apis = TestApiRegistry.from( + [dynatraceApiRef, mockDynatraceApi], + [configApiRef, new ConfigReader({ dynatrace: { baseUrl: '__dynatrace__' } })], +); + +describe('SyntheticsCard', () => { + it('renders the card with Synthetics data', async () => { + mockDynatraceApi.getDynatraceSyntheticFailures = jest + .fn() + .mockResolvedValue({ + locationsExecutionResults: [ + { + locationId: '__location__', + requestResults: [{ startTimestamp: 0 }], + }, + ], + }); + const rendered = await renderInTestApp( + + + , + , + ); + expect( + await rendered.findByText('View this Synthetic in Dynatrace'), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx index 0fa1a148e9..de04f73e36 100644 --- a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx +++ b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.test.tsx @@ -14,5 +14,52 @@ * limitations under the License. */ import React from 'react'; +import { SyntheticsLocation } from './SyntheticsLocation'; +import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { dynatraceApiRef } from '../../../api'; +import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; +import { configApiRef } from '@backstage/core-plugin-api'; -describe('SyntheticsLocation', () => {}); +const mockDynatraceApi = { + getDynatraceSyntheticLocationInfo: jest.fn(), +}; +const apis = TestApiRegistry.from( + [dynatraceApiRef, mockDynatraceApi], + [configApiRef, new ConfigReader({ dynatrace: { baseUrl: '__dynatrace__' } })], +); + +describe('SyntheticsLocation', () => { + it('renders the SyntheticsLocation chip - recent failure', async () => { + mockDynatraceApi.getDynatraceSyntheticLocationInfo = jest + .fn() + .mockResolvedValue({ name: '__location__' }); + const rendered = await renderInTestApp( + + + , + , + ); + expect(await rendered.findByText(/failed/)).toBeInTheDocument(); + }); + it('renders the SyntheticsLocation chip - no failures', async () => { + mockDynatraceApi.getDynatraceSyntheticLocationInfo = jest + .fn() + .mockResolvedValue({ name: '__location__' }); + const rendered = await renderInTestApp( + + + , + , + ); + expect(await rendered.findByText(/__location__/)).toBeInTheDocument(); + expect(await rendered.queryByText(/failed/)).not.toBeInTheDocument(); + }); +}); diff --git a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.tsx b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.tsx index 957f1c34f5..e72995158c 100644 --- a/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.tsx +++ b/plugins/dynatrace/src/components/Synthetics/SyntheticsLocation/SyntheticsLocation.tsx @@ -28,26 +28,20 @@ type SyntheticsLocationProps = { key: string; }; -const failedInLast24Hours = (timestamp: Date): Boolean => { - return timestamp > new Date(new Date().getTime() - 1000 * 60 * 60 * 24); -}; - -const failedInLast6Hours = (timestamp: Date): Boolean => { - return timestamp > new Date(new Date().getTime() - 1000 * 60 * 60 * 6); -}; - -const failedinLastHour = (timestamp: Date): Boolean => { - return timestamp > new Date(new Date().getTime() - 1000 * 60 * 60); +const failedInLastXHours = (timestamp: Date, offset: number): Boolean => { + if (offset < 0 || offset > 24) + throw new Error('offset must be between 0 and 24'); + return timestamp > new Date(new Date().getTime() - 1000 * 60 * 60 * offset); }; const chipColor = (timestamp: Date): string => { - if (failedinLastHour(timestamp)) { + if (failedInLastXHours(timestamp, 1)) { return 'salmon'; } - if (failedInLast6Hours(timestamp)) { + if (failedInLastXHours(timestamp, 6)) { return 'sandybrown'; } - if (failedInLast24Hours(timestamp)) { + if (failedInLastXHours(timestamp, 24)) { return 'palegoldenrod'; } return 'lightgreen'; @@ -71,8 +65,8 @@ export const SyntheticsLocation = (props: SyntheticsLocationProps) => { return (