From e065238f78d43dbefd9f429b56bc764c79101df0 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Thu, 28 Jul 2022 12:27:22 +0200 Subject: [PATCH] SONAR-16703 [891721] Label does not convey purpose of control --- .../projectInformation/meta/MetaKey.tsx | 6 +- .../meta/__tests__/MetaKey-test.tsx | 4 +- .../js/apps/account/__tests__/Account-it.tsx | 2 +- .../__snapshots__/clipboard-test.tsx.snap | 52 ---------- .../controls/__tests__/clipboard-test.tsx | 98 +++++++++---------- .../main/js/components/controls/clipboard.tsx | 25 +++-- .../resources/org/sonar/l10n/core.properties | 1 + 7 files changed, 75 insertions(+), 113 deletions(-) delete mode 100644 server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/clipboard-test.tsx.snap diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaKey.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaKey.tsx index b7e197a83de..9457e903dfd 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaKey.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaKey.tsx @@ -38,7 +38,11 @@ export default function MetaKey({ componentKey, qualifier }: MetaKeyProps) { type="text" value={componentKey} /> - + ); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaKey-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaKey-test.tsx index d081a6d7c7c..dee344e4a88 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaKey-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaKey-test.tsx @@ -28,7 +28,9 @@ it('should render correctly', () => { expect( screen.getByLabelText(`overview.project_key.${ComponentQualifier.Project}`) ).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'overview.project_key.click_to_copy' }) + ).toBeInTheDocument(); }); function renderMetaKey(props: Partial = {}) { diff --git a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx index 236dafea32f..32ca9eabc17 100644 --- a/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx +++ b/server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx @@ -284,7 +284,7 @@ describe('security page', () => { expect( await screen.findByText(`users.tokens.new_token_created.${newTokenName}`) ).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'copy' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument(); const lastTokenCreated = tokenMock.getTokens().pop(); expect(lastTokenCreated).toBeDefined(); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/clipboard-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/clipboard-test.tsx.snap deleted file mode 100644 index de2081cb464..00000000000 --- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/clipboard-test.tsx.snap +++ /dev/null @@ -1,52 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ClipboardBase should display correctly 1`] = ` - -`; - -exports[`ClipboardButton should display correctly 1`] = ` - - - -`; - -exports[`ClipboardButton should render a custom label if provided 1`] = ` - - - -`; - -exports[`ClipboardIconButton should display correctly 1`] = ` - - - -`; diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx index bf2c040aa21..075406a6a44 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/clipboard-test.tsx @@ -17,26 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { mount, shallow } from 'enzyme'; +import { screen } from '@testing-library/react'; import * as React from 'react'; -import { Button } from '../buttons'; -import { ClipboardBase, ClipboardButton, ClipboardIconButton } from '../clipboard'; - -const constructor = jest.fn(); -const destroy = jest.fn(); -const on = jest.fn(); - -jest.mock( - 'clipboard', - () => - function(...args: any) { - constructor(...args); - return { - destroy, - on - }; - } -); +import { renderComponent } from '../../../helpers/testReactTestingUtils'; +import { + ClipboardBase, + ClipboardButton, + ClipboardButtonProps, + ClipboardIconButton, + ClipboardIconButtonProps +} from '../clipboard'; beforeAll(() => { jest.useFakeTimers(); @@ -49,61 +39,69 @@ afterAll(() => { describe('ClipboardBase', () => { it('should display correctly', () => { - const children = jest.fn().mockReturnValue(); - const wrapper = shallowRender(children); - const instance = wrapper.instance(); - expect(wrapper).toMatchSnapshot(); - instance.handleSuccessCopy(); - expect(children).toBeCalledWith({ copySuccess: true, setCopyButton: instance.setCopyButton }); - jest.runAllTimers(); - expect(children).toBeCalledWith({ copySuccess: false, setCopyButton: instance.setCopyButton }); + renderClipboardBase(); + expect(screen.getByText('click to copy')).toBeInTheDocument(); }); it('should allow its content to be copied', () => { - const wrapper = mountRender(({ setCopyButton }) => ( - - )); - const button = wrapper.find('button').getDOMNode(); - const instance = wrapper.instance(); + renderClipboardBase(); + const button = screen.getByRole('button'); + button.click(); - expect(constructor).toBeCalledWith(button); - expect(on).toBeCalledWith('success', instance.handleSuccessCopy); + expect(screen.getByText('copied')).toBeInTheDocument(); - jest.clearAllMocks(); + jest.runAllTimers(); - wrapper.unmount(); - expect(destroy).toBeCalled(); + expect(screen.getByText('click to copy')).toBeInTheDocument(); }); - function shallowRender(children?: ClipboardBase['props']['children']) { - return shallow({children || (() => null)}); - } - - function mountRender(children?: ClipboardBase['props']['children']) { - return mount({children || (() => null)}); + function renderClipboardBase(props: Partial = {}) { + return renderComponent( + + {({ setCopyButton, copySuccess }) => ( + + {copySuccess ? 'copied' : 'click to copy'} + + )} + + ); } }); describe('ClipboardButton', () => { it('should display correctly', () => { - expect(shallowRender()).toMatchSnapshot(); + renderClipboardButton(); + expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument(); }); it('should render a custom label if provided', () => { - expect(shallowRender('Foo Bar')).toMatchSnapshot(); + renderClipboardButton({ children: 'custom label' }); + expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument(); + expect(screen.getByText('custom label')).toBeInTheDocument(); + }); + + it('should render a custom aria-label if provided', () => { + renderClipboardButton({ 'aria-label': 'custom label' }); + expect(screen.getByRole('button', { name: 'custom label' })).toBeInTheDocument(); }); - function shallowRender(children?: React.ReactNode) { - return shallow({children}).dive(); + function renderClipboardButton(props: Partial = {}) { + return renderComponent(); } }); describe('ClipboardIconButton', () => { it('should display correctly', () => { - expect(shallowRender()).toMatchSnapshot(); + renderClipboardIconButton(); + expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument(); + }); + + it('should render a custom aria-label if provided', () => { + renderClipboardIconButton({ 'aria-label': 'custom label' }); + expect(screen.getByRole('button', { name: 'custom label' })).toBeInTheDocument(); }); - function shallowRender() { - return shallow().dive(); + function renderClipboardIconButton(props: Partial = {}) { + return renderComponent(); } }); diff --git a/server/sonar-web/src/main/js/components/controls/clipboard.tsx b/server/sonar-web/src/main/js/components/controls/clipboard.tsx index 97d29745cea..c72336dc7fa 100644 --- a/server/sonar-web/src/main/js/components/controls/clipboard.tsx +++ b/server/sonar-web/src/main/js/components/controls/clipboard.tsx @@ -32,13 +32,14 @@ export interface State { interface RenderProps { setCopyButton: (node: HTMLElement | null) => void; copySuccess: boolean; + role: string; } -interface BaseProps { +interface Props { children: (props: RenderProps) => React.ReactNode; } -export class ClipboardBase extends React.PureComponent { +export class ClipboardBase extends React.PureComponent { private clipboard?: Clipboard; private copyButton?: HTMLElement | null; mounted = false; @@ -87,18 +88,25 @@ export class ClipboardBase extends React.PureComponent { render() { return this.props.children({ setCopyButton: this.setCopyButton, - copySuccess: this.state.copySuccess + copySuccess: this.state.copySuccess, + role: 'button' }); } } -interface ButtonProps { +export interface ClipboardButtonProps { + 'aria-label'?: string; className?: string; copyValue: string; children?: React.ReactNode; } -export function ClipboardButton({ className, children, copyValue }: ButtonProps) { +export function ClipboardButton({ + className, + children, + copyValue, + 'aria-label': ariaLabel +}: ClipboardButtonProps) { return ( {({ setCopyButton, copySuccess }) => ( @@ -106,7 +114,8 @@ export function ClipboardButton({ className, children, copyValue }: ButtonProps)