copy-text-button: refactoring tests and error handling

Signed-off-by: Vincenzo Scamporlino <me@vinzscam.dev>
This commit is contained in:
Vincenzo Scamporlino
2021-08-19 17:25:58 +02:00
parent de1604fc31
commit 9606d34fde
2 changed files with 48 additions and 17 deletions
@@ -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('<CopyTextButton />', () => {
it('renders without exploding', async () => {
const { getByTestId } = await renderInTestApp(
const { getByTitle, queryByText } = await renderInTestApp(
<ApiProvider apis={apiRegistry}>
<CopyTextButton {...props} />
</ApiProvider>,
);
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(
<ApiProvider apis={apiRegistry}>
<CopyTextButton {...props} />
@@ -84,4 +93,18 @@ describe('<CopyTextButton />', () => {
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(
<ApiProvider apis={apiRegistry}>
<CopyTextButton {...props} />
</ApiProvider>,
);
expect(apiRegistry.get(errorApiRef)?.post).toHaveBeenCalledWith(error);
});
});
@@ -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}
>
<IconButton onClick={handleCopyClick}>
<CopyIcon data-testid="copy-button" />
<CopyIcon />
</IconButton>
</Tooltip>
</>