From 9606d34fde13fd4535495ce605f0c0da0c1a7a2c Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Thu, 19 Aug 2021 17:25:58 +0200 Subject: [PATCH] copy-text-button: refactoring tests and error handling Signed-off-by: Vincenzo Scamporlino --- .../CopyTextButton/CopyTextButton.test.tsx | 51 ++++++++++++++----- .../CopyTextButton/CopyTextButton.tsx | 14 +++-- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx index 9e3a94ee45..b84d3919e8 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.test.tsx @@ -19,12 +19,9 @@ import { fireEvent } from '@testing-library/react'; import { act } from 'react-dom/test-utils'; import { renderInTestApp } from '@backstage/test-utils'; import { CopyTextButton } from './CopyTextButton'; -import { ApiRegistry, ApiProvider } from '@backstage/core-app-api'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; import { errorApiRef, ErrorApi } from '@backstage/core-plugin-api'; -// should we add this to dev dependencies? -// (already a dependency of react-use) -// eslint-disable-next-line import/no-extraneous-dependencies -import copy from 'copy-to-clipboard'; +import { useCopyToClipboard } from 'react-use'; jest.mock('popper.js', () => { const PopperJS = jest.requireActual('popper.js'); @@ -37,6 +34,17 @@ jest.mock('popper.js', () => { }; }); +jest.mock('react-use', () => { + const original = jest.requireActual('react-use'); + + return { + ...original, + useCopyToClipboard: jest + .fn() + .mockImplementation(original.useCopyToClipboard), + }; +}); + const props = { text: 'mockText', tooltipDelay: 2, @@ -47,29 +55,30 @@ const apiRegistry = ApiRegistry.from([ [ errorApiRef, { - post(error) { - throw error; - }, + post: jest.fn(), error$: jest.fn(), } as ErrorApi, ], ]); -jest.mock('copy-to-clipboard', () => jest.fn()); - describe('', () => { it('renders without exploding', async () => { - const { getByTestId } = await renderInTestApp( + const { getByTitle, queryByText } = await renderInTestApp( , ); - expect(getByTestId('copy-button')).toBeInTheDocument(); + expect(getByTitle('mockTooltip')).toBeInTheDocument(); + expect(queryByText('mockTooltip')).not.toBeInTheDocument(); }); - it('displays tooltip on click', async () => { + it('displays tooltip and copy the text on click', async () => { jest.useFakeTimers(); - document.execCommand = jest.fn(); + + const spy = useCopyToClipboard as jest.Mock; + const copy = jest.fn(); + spy.mockReturnValue([{}, copy]); + const rendered = await renderInTestApp( @@ -84,4 +93,18 @@ describe('', () => { rendered.getByText('mockTooltip'); jest.useRealTimers(); }); + + it('reports copy errors', async () => { + const spy = useCopyToClipboard as jest.Mock; + + const error = new Error('just an error'); + spy.mockReturnValue([{ error }, jest.fn()]); + + await renderInTestApp( + + + , + ); + expect(apiRegistry.get(errorApiRef)?.post).toHaveBeenCalledWith(error); + }); }); diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx index b7120f6ad3..cc0ecaf76f 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx @@ -14,10 +14,11 @@ * limitations under the License. */ +import { errorApiRef, useApi } from '@backstage/core-plugin-api'; import { IconButton, Tooltip } from '@material-ui/core'; import CopyIcon from '@material-ui/icons/FileCopy'; import PropTypes from 'prop-types'; -import React, { MouseEventHandler, useState } from 'react'; +import React, { MouseEventHandler, useEffect, useState } from 'react'; import { useCopyToClipboard } from 'react-use'; /** @@ -51,8 +52,15 @@ export const CopyTextButton = (props: Props) => { ...defaultProps, ...props, }; + const errorApi = useApi(errorApiRef); const [open, setOpen] = useState(false); - const [, copyToClipboard] = useCopyToClipboard(); + const [{ error }, copyToClipboard] = useCopyToClipboard(); + + useEffect(() => { + if (error) { + errorApi.post(error); + } + }, [error, errorApi]); const handleCopyClick: MouseEventHandler = e => { e.stopPropagation(); @@ -71,7 +79,7 @@ export const CopyTextButton = (props: Props) => { open={open} > - +