From 5b29c84a1cb25772e3749dfe6b5539135583e6fe Mon Sep 17 00:00:00 2001 From: Niklas Granander Date: Fri, 30 Jul 2021 15:20:38 +0200 Subject: [PATCH] Add tests for builds page and XcmetricsLayout Signed-off-by: Niklas Granander --- .../src/api/__mocks__/XcmetricsClient.ts | 9 +- .../BuildListComponent.test.tsx | 61 +++++++ .../BuildListFilterComponent.test.tsx | 158 ++++++++++++++++++ .../BuildListFilterComponent.tsx | 14 +- .../DatePickerComponent.test.tsx | 53 ++++++ .../DatePickerComponent.tsx | 1 + .../XcmetricsLayout/XcmetricsLayout.test.tsx | 48 ++++++ plugins/xcmetrics/src/plugin.test.ts | 3 +- 8 files changed, 337 insertions(+), 10 deletions(-) create mode 100644 plugins/xcmetrics/src/components/BuildListComponent/BuildListComponent.test.tsx create mode 100644 plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.test.tsx create mode 100644 plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.test.tsx create mode 100644 plugins/xcmetrics/src/components/XcmetricsLayout/XcmetricsLayout.test.tsx diff --git a/plugins/xcmetrics/src/api/__mocks__/XcmetricsClient.ts b/plugins/xcmetrics/src/api/__mocks__/XcmetricsClient.ts index cacb8fb2b1..430fbb1006 100644 --- a/plugins/xcmetrics/src/api/__mocks__/XcmetricsClient.ts +++ b/plugins/xcmetrics/src/api/__mocks__/XcmetricsClient.ts @@ -28,7 +28,7 @@ export const mockBuild = { day: '2021-01-01', compilationEndTimestamp: '2021-01-01T00:00:01Z', tag: '', - projectName: 'Project', + projectName: 'ProjectName', compilationEndTimestampMicroseconds: 1, errorCount: 1, id: 'buildId', @@ -67,10 +67,13 @@ export const XcmetricsClient: XcmetricsApi = { _perPage?: number, ) => { return Promise.resolve({ - items: [mockBuild], + items: [ + mockBuild, + { ...mockBuild, buildStatus: 'failed', projectName: 'ProjectName2' }, + ], metadata: { per: 10, - total: 1, + total: 2, page: 1, }, }); diff --git a/plugins/xcmetrics/src/components/BuildListComponent/BuildListComponent.test.tsx b/plugins/xcmetrics/src/components/BuildListComponent/BuildListComponent.test.tsx new file mode 100644 index 0000000000..3818d51a16 --- /dev/null +++ b/plugins/xcmetrics/src/components/BuildListComponent/BuildListComponent.test.tsx @@ -0,0 +1,61 @@ +/* + * Copyright 2021 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. + */ +import React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { BuildListComponent } from './BuildListComponent'; +import { xcmetricsApiRef } from '../../api'; + +jest.mock('../../api/XcmetricsClient'); +const client = require('../../api/XcmetricsClient'); + +jest.mock('../BuildListFilterComponent', () => ({ + BuildListFilterComponent: () => 'BuildListFilterComponent', +})); + +describe('BuildListComponent', () => { + it('should render', async () => { + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText('Builds')).toBeInTheDocument(); + expect( + rendered.getByText(client.mockBuild.projectName), + ).toBeInTheDocument(); + }); + + it('should show errors', async () => { + const message = 'error'; + client.XcmetricsClient.getFilteredBuilds = jest + .fn() + .mockRejectedValue({ message }); + + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText(message)).toBeInTheDocument(); + }); +}); diff --git a/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.test.tsx b/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.test.tsx new file mode 100644 index 0000000000..eac5775d77 --- /dev/null +++ b/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.test.tsx @@ -0,0 +1,158 @@ +/* + * Copyright 2021 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. + */ +import React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import userEvent from '@testing-library/user-event'; +import { BuildListFilterComponent } from './BuildListFilterComponent'; +import { BuildFilters, xcmetricsApiRef } from '../../api'; +import { RenderResult } from '@testing-library/react'; + +jest.mock('../../api/XcmetricsClient'); +const client = require('../../api/XcmetricsClient'); + +jest.mock('../DatePickerComponent', () => ({ + DatePickerComponent: () => 'DatePickerComponent', +})); + +const initialValues = { + from: '2020-07-30', + to: '2021-07-30', +}; + +const renderWithFiltersVisible = async ( + callback?: (filters: BuildFilters) => void, +) => { + const rendered = await renderInTestApp( + + + , + ); + + userEvent.click(rendered.getByLabelText('show filters')); + return rendered; +}; + +const setStatusFilter = async (rendered: RenderResult, option: string) => { + const statusSelect = rendered.getAllByTestId('select')[0]; + userEvent.click(statusSelect); + userEvent.click((await rendered.findAllByText(option))[0]); +}; + +const setProjectFilter = async (rendered: RenderResult, option: string) => { + const statusSelect = rendered.getAllByTestId('select')[1]; + userEvent.click(statusSelect); + const options = await rendered.findAllByText(option); + userEvent.click(options[options.length - 1]); +}; + +describe('BuildListFilterComponent', () => { + it('should render', async () => { + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText('Filters (0)')).toBeInTheDocument(); + }); + + it('should toggle between showing and hiding filters', async () => { + const rendered = await renderWithFiltersVisible(); + + expect( + (await rendered.findAllByText('DatePickerComponent')).length, + ).toEqual(2); + expect(await rendered.findByText('Status')).toBeInTheDocument(); + expect(await rendered.findByText('Project')).toBeInTheDocument(); + + userEvent.click(rendered.getByLabelText('hide filters')); + expect(rendered.queryByText('DatePickerComponent')).toBeNull(); + expect(rendered.queryByText('Status')).toBeNull(); + expect(rendered.queryByText('Project')).toBeNull(); + }); + + it('should load projects', async () => { + const callback = jest.fn(); + const rendered = await renderWithFiltersVisible(callback); + userEvent.click((await rendered.findAllByText('All'))[1]); + + expect( + await rendered.findByText(client.mockBuild.projectName), + ).toBeInTheDocument(); + }); + + it('should call back with a status when status is selected', async () => { + const callback = jest.fn(); + const rendered = await renderWithFiltersVisible(callback); + + await setStatusFilter(rendered, 'Succeeded'); + expect(callback).toBeCalledWith({ + ...initialValues, + buildStatus: 'succeeded', + }); + + await setStatusFilter(rendered, 'All'); + expect(callback).toBeCalledWith(initialValues); + }); + + it('should call back with a project when project is selected', async () => { + const callback = jest.fn(); + const rendered = await renderWithFiltersVisible(callback); + + await setProjectFilter(rendered, client.mockBuild.projectName); + expect(callback).toBeCalledWith({ + ...initialValues, + project: client.mockBuild.projectName, + }); + + await setProjectFilter(rendered, 'All'); + expect(callback).toBeCalledWith(initialValues); + }); + + it('should display a count of active (changed) filters', async () => { + const rendered = await renderWithFiltersVisible(); + + await setStatusFilter(rendered, 'Failed'); + await setProjectFilter(rendered, client.mockBuild.projectName); + + expect(await rendered.findByText('Filters (2)')).toBeInTheDocument(); + }); + + it('should clear all filters', async () => { + const callback = jest.fn(); + const rendered = await renderWithFiltersVisible(callback); + + await setStatusFilter(rendered, 'Failed'); + await setProjectFilter(rendered, client.mockBuild.projectName); + + callback.mockClear(); + userEvent.click(await rendered.findByText('Clear all')); + + expect(callback).toHaveBeenCalledWith(initialValues); + expect(await rendered.findByText('Filters (0)')).toBeInTheDocument(); + }); +}); diff --git a/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.tsx b/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.tsx index 4f6604702e..ff45e15925 100644 --- a/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.tsx +++ b/plugins/xcmetrics/src/components/BuildListFilterComponent/BuildListFilterComponent.tsx @@ -52,15 +52,17 @@ export const BuildListFilterComponent = ({ useEffect(() => onFilterChange(values), [onFilterChange, values]); - const numFilters = - Number(values.from !== initialValues.from) + - Number(values.to !== initialValues.to) + - Number(!!values.buildStatus) + - Number(!!values.project); + const numFilters = Object.keys(values).reduce((sum, key) => { + const filtersKey = key as keyof BuildFilters; + return sum + Number(values[filtersKey] !== initialValues[filtersKey]); + }, 0); const title = ( <> - setOpen(!open)} aria-label="filter list"> + setOpen(!open)} + aria-label={`${open ? 'hide' : 'show'} filters`} + > Filters ({numFilters}) diff --git a/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.test.tsx b/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.test.tsx new file mode 100644 index 0000000000..74633fe786 --- /dev/null +++ b/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.test.tsx @@ -0,0 +1,53 @@ +/* + * Copyright 2021 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. + */ +import React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { DatePickerComponent } from './DatePickerComponent'; +import userEvent from '@testing-library/user-event'; + +describe('DatePickerComponent', () => { + it('should render', async () => { + const label = 'label'; + const rendered = await renderInTestApp( + , + ); + expect(rendered.getByText(label)).toBeInTheDocument(); + }); + + it('should accept a date', async () => { + const label = 'label'; + const callback = jest.fn(); + const rendered = await renderInTestApp( + , + ); + const input = rendered.getByLabelText(label); + + userEvent.type(input, '2020-02-02'); + expect(callback).toBeCalledWith('2020-02-02'); + }); + + it('should not accept non date', async () => { + const label = 'label'; + const callback = jest.fn(); + const rendered = await renderInTestApp( + , + ); + const input = rendered.getByLabelText(label); + + userEvent.type(input, 'test'); + expect(callback).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.tsx b/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.tsx index a5b1fbb346..854fbbd87f 100644 --- a/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.tsx +++ b/plugins/xcmetrics/src/components/DatePickerComponent/DatePickerComponent.tsx @@ -74,6 +74,7 @@ export const DatePickerComponent = ({
{label} onDateChange?.(event.target.value)} diff --git a/plugins/xcmetrics/src/components/XcmetricsLayout/XcmetricsLayout.test.tsx b/plugins/xcmetrics/src/components/XcmetricsLayout/XcmetricsLayout.test.tsx new file mode 100644 index 0000000000..2ce634dc11 --- /dev/null +++ b/plugins/xcmetrics/src/components/XcmetricsLayout/XcmetricsLayout.test.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2021 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. + */ +import React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { XcmetricsLayout } from './XcmetricsLayout'; +import { xcmetricsApiRef } from '../../api'; + +jest.mock('../../api/XcmetricsClient'); +const client = require('../../api/XcmetricsClient'); + +jest.mock('../OverviewComponent', () => ({ + OverviewComponent: () => 'OverviewComponent', +})); + +jest.mock('../BuildListComponent', () => ({ + BuildListComponent: () => 'BuildListComponent', +})); + +describe('XcmetricsLayout', () => { + it('should render', async () => { + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByText('Overview')).toBeInTheDocument(); + expect(rendered.getByText('Builds')).toBeInTheDocument(); + + expect(rendered.getByText('OverviewComponent')).toBeInTheDocument(); + }); +}); diff --git a/plugins/xcmetrics/src/plugin.test.ts b/plugins/xcmetrics/src/plugin.test.ts index 055c647dd9..a390e8aeb7 100644 --- a/plugins/xcmetrics/src/plugin.test.ts +++ b/plugins/xcmetrics/src/plugin.test.ts @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { xcmetricsPlugin } from './plugin'; +import { XcmetricsPage, xcmetricsPlugin } from './plugin'; describe('xcmetrics', () => { it('should export plugin', () => { expect(xcmetricsPlugin).toBeDefined(); + expect(XcmetricsPage).toBeDefined(); }); });