From cbc8d147cdfb08ec420deabd78f842562e9e21ad Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Fri, 14 Apr 2023 11:43:22 +0200 Subject: [PATCH] SONAR-19018 Migrate the report actions to the new UI --- .../branches/BranchOverviewRenderer.tsx | 7 +- .../ComponentReportActionsRenderer.tsx | 104 ++++++----- .../__tests__/ComponentReportActions-test.tsx | 136 ++++++++------ .../ComponentReportActionsRenderer-test.tsx | 55 ------ ...mponentReportActionsRenderer-test.tsx.snap | 166 ------------------ 5 files changed, 148 insertions(+), 320 deletions(-) delete mode 100644 server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActionsRenderer-test.tsx delete mode 100644 server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ComponentReportActionsRenderer-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx index fa9ea15bc17..5cc97dbd2b6 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { LargeCenteredLayout } from 'design-system'; import * as React from 'react'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; import { parseDate } from '../../../helpers/dates'; @@ -84,8 +85,8 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp detectedCIOnLastAnalysis={detectedCIOnLastAnalysis} projectBinding={projectBinding} /> -
-
+ +
{projectIsEmpty ? ( @@ -127,7 +128,7 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp
)}
-
+ ); } diff --git a/server/sonar-web/src/main/js/components/controls/ComponentReportActionsRenderer.tsx b/server/sonar-web/src/main/js/components/controls/ComponentReportActionsRenderer.tsx index c2deafcd193..8bea9571e5c 100644 --- a/server/sonar-web/src/main/js/components/controls/ComponentReportActionsRenderer.tsx +++ b/server/sonar-web/src/main/js/components/controls/ComponentReportActionsRenderer.tsx @@ -17,11 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + ButtonSecondary, + ChevronDownIcon, + Dropdown, + ItemButton, + ItemDownload, + PopupPlacement, + PopupZLevel, +} from 'design-system'; import * as React from 'react'; import { getReportUrl } from '../../api/component-report'; -import Dropdown from '../../components/controls/Dropdown'; -import { Button } from '../../components/controls/buttons'; -import DropdownIcon from '../../components/icons/DropdownIcon'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { Branch } from '../../types/branch-like'; import { Component } from '../../types/types'; @@ -37,65 +43,69 @@ export interface ComponentReportActionsRendererProps { handleUnsubscription: () => void; } -export default function ComponentReportActionsRenderer(props: ComponentReportActionsRendererProps) { - const { branch, component, frequency, subscribed, canSubscribe, currentUserHasEmail } = props; +const getSubscriptionText = ({ + currentUserHasEmail, + frequency, + subscribed, +}: Pick< + ComponentReportActionsRendererProps, + 'currentUserHasEmail' | 'frequency' | 'subscribed' +>) => { + if (!currentUserHasEmail) { + return translate('component_report.no_email_to_subscribe'); + } - const renderDownloadButton = (simple = false) => { - return ( - !!s).join(' - ')} - href={getReportUrl(component.key, branch?.name)} - target="_blank" - rel="noopener noreferrer" - > - {simple - ? translate('download_verb') - : translateWithParameters( - 'component_report.download', - translate('qualifier', component.qualifier).toLowerCase() - )} - - ); - }; + const translationKey = subscribed + ? 'component_report.unsubscribe_x' + : 'component_report.subscribe_x'; + const frequencyTranslation = translate('report.frequency', frequency).toLowerCase(); - const renderSubscriptionButton = () => { - if (!currentUserHasEmail) { - return ( - {translate('component_report.no_email_to_subscribe')} - ); - } + return translateWithParameters(translationKey, frequencyTranslation); +}; - const translationKey = subscribed - ? 'component_report.unsubscribe_x' - : 'component_report.subscribe_x'; - const onClickHandler = subscribed ? props.handleUnsubscription : props.handleSubscription; - const frequencyTranslation = translate('report.frequency', frequency).toLowerCase(); +export default function ComponentReportActionsRenderer(props: ComponentReportActionsRendererProps) { + const { branch, component, frequency, subscribed, canSubscribe, currentUserHasEmail } = props; - return ( - - {translateWithParameters(translationKey, frequencyTranslation)} - - ); - }; + const downloadName = [component.name, branch?.name, 'PDF Report.pdf'] + .filter((s) => !!s) + .join(' - '); + const reportUrl = getReportUrl(component.key, branch?.name); return canSubscribe ? ( -
  • {renderDownloadButton(true)}
  • -
  • {renderSubscriptionButton()}
  • - + <> + + {translate('download_verb')} + + + {getSubscriptionText({ currentUserHasEmail, frequency, subscribed })} + + } > - + +
    ) : ( - renderDownloadButton() + + {translateWithParameters( + 'component_report.download', + translate('qualifier', component.qualifier).toLowerCase() + )} + ); } diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx index 5bcf7725e7e..aaf3982d86c 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx +++ b/server/sonar-web/src/main/js/components/controls/__tests__/ComponentReportActions-test.tsx @@ -17,19 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { shallow } from 'enzyme'; +import { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import * as React from 'react'; import { getReportStatus, subscribeToEmailReport, unsubscribeFromEmailReport, } from '../../../api/component-report'; -import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { mockBranch } from '../../../helpers/mocks/branch-like'; import { mockComponent } from '../../../helpers/mocks/component'; import { mockComponentReportStatus } from '../../../helpers/mocks/component-report'; -import { mockAppState, mockCurrentUser } from '../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../helpers/testUtils'; +import { mockAppState, mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks'; +import { renderApp } from '../../../helpers/testReactTestingUtils'; import { ComponentQualifier } from '../../../types/component'; import { ComponentReportActions } from '../ComponentReportActions'; @@ -49,76 +49,114 @@ jest.mock('../../../helpers/system', () => ({ getBaseUrl: jest.fn().mockReturnValue('baseUrl'), })); -jest.mock('../../../helpers/globalMessages', () => ({ - addGlobalSuccessMessage: jest.fn(), -})); - beforeEach(jest.clearAllMocks); -it('should not render anything', async () => { - // loading - expect(shallowRender().type()).toBeNull(); +it('should not render anything when no status', async () => { + jest.mocked(getReportStatus).mockRejectedValueOnce('Nope'); + + renderComponentReportActions(); + + // Loading + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + + await new Promise(setImmediate); // Make sure we wait until we're done loading // No status - (getReportStatus as jest.Mock).mockResolvedValueOnce(undefined); - const w1 = shallowRender(); - await waitAndUpdate(w1); - expect(w1.type()).toBeNull(); - - // Branch purgeable - const w2 = shallowRender({ branch: mockBranch({ excludedFromPurge: false }) }); - await waitAndUpdate(w2); - expect(w2.type()).toBeNull(); - - // no governance - const w3 = shallowRender({ appState: mockAppState({ qualifiers: [] }) }); - await waitAndUpdate(w3); - expect(w3.type()).toBeNull(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); -it('should call for status properly', async () => { - const component = mockComponent(); - const branch = mockBranch(); +it('should not render anything when branch is purgeable', async () => { + renderComponentReportActions({ + branch: mockBranch({ excludedFromPurge: false }), + }); - const wrapper = shallowRender({ component, branch }); + await new Promise(setImmediate); // Make sure we wait until we're done loading - await waitAndUpdate(wrapper); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); +}); - expect(getReportStatus).toHaveBeenCalledWith(component.key, branch.name); +it('should not render anything without governance', async () => { + renderComponentReportActions({ appState: mockAppState({ qualifiers: [] }) }); + + await new Promise(setImmediate); // Make sure we wait until we're done loading + + expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); -it('should handle subscription', async () => { +it('should allow user to (un)subscribe', async () => { + jest + .mocked(getReportStatus) + .mockResolvedValueOnce(mockComponentReportStatus({ globalFrequency: 'monthly' })) + .mockResolvedValueOnce( + mockComponentReportStatus({ subscribed: true, globalFrequency: 'monthly' }) + ); + + const user = userEvent.setup(); const component = mockComponent(); const branch = mockBranch(); - const wrapper = shallowRender({ component, branch }); - await wrapper.instance().handleSubscribe(); + renderComponentReportActions({ + component, + branch, + currentUser: mockLoggedInUser({ email: 'igot@nEmail.address' }), + }); + + expect(getReportStatus).toHaveBeenCalledWith(component.key, branch.name); + + const button = await screen.findByRole('button', { + name: 'component_report.report.qualifier.TRK', + }); + expect(button).toBeInTheDocument(); + await user.click(button); + + expect(screen.getByText('download_verb')).toBeInTheDocument(); + + // Subscribe! + const subscribeButton = screen.getByText('component_report.subscribe_x.report.frequency.monthly'); + expect(subscribeButton).toBeInTheDocument(); + + await user.click(subscribeButton); expect(subscribeToEmailReport).toHaveBeenCalledWith(component.key, branch.name); - expect(addGlobalSuccessMessage).toHaveBeenCalledWith( - 'component_report.subscribe_x_success.report.frequency..qualifier.trk' - ); -}); + expect(await screen.findByRole('status')).toBeInTheDocument(); -it('should handle unsubscription', async () => { - const component = mockComponent(); - const branch = mockBranch(); - const wrapper = shallowRender({ component, branch }); + await new Promise(setImmediate); - await waitAndUpdate(wrapper); + // And unsubscribe! + await user.click(button); - wrapper.setState({ status: mockComponentReportStatus({ componentFrequency: 'compfreq' }) }); + const unsubscribeButton = screen.getByText( + 'component_report.unsubscribe_x.report.frequency.monthly' + ); + expect(unsubscribeButton).toBeInTheDocument(); - await wrapper.instance().handleUnsubscribe(); + await user.click(unsubscribeButton); expect(unsubscribeFromEmailReport).toHaveBeenCalledWith(component.key, branch.name); - expect(addGlobalSuccessMessage).toHaveBeenCalledWith( - 'component_report.unsubscribe_x_success.report.frequency.compfreq.qualifier.trk' + expect(screen.getAllByRole('status')).toHaveLength(2); +}); + +it('should prevent user to subscribe if no email', async () => { + const user = userEvent.setup(); + + renderComponentReportActions({ currentUser: mockLoggedInUser({ email: undefined }) }); + + await new Promise(setImmediate); + + await user.click( + await screen.findByRole('button', { + name: 'component_report.report.qualifier.TRK', + }) ); + + const subscribeButton = screen.getByText('component_report.no_email_to_subscribe'); + expect(subscribeButton).toBeInTheDocument(); + expect(subscribeButton).toBeDisabled(); }); -function shallowRender(props: Partial = {}) { - return shallow( +function renderComponentReportActions(props: Partial = {}) { + return renderApp( + '/', { - expect(shallowRender({ canSubscribe: false })).toMatchSnapshot('cannot subscribe'); - expect(shallowRender({ canSubscribe: true, subscribed: false })).toMatchSnapshot( - 'can subscribe, not subscribed' - ); - expect(shallowRender({ canSubscribe: true, subscribed: true })).toMatchSnapshot( - 'can subscribe, subscribed' - ); - expect(shallowRender({ canSubscribe: true, currentUserHasEmail: false })).toMatchSnapshot( - 'current user without email' - ); - expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('not a portfolio'); -}); - -function shallowRender(props: Partial = {}) { - return shallow( - - ); -} diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ComponentReportActionsRenderer-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ComponentReportActionsRenderer-test.tsx.snap deleted file mode 100644 index 65a22152a37..00000000000 --- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ComponentReportActionsRenderer-test.tsx.snap +++ /dev/null @@ -1,166 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: can subscribe, not subscribed 1`] = ` - -
  • - - download_verb - -
  • -
  • - - component_report.subscribe_x.report.frequency.weekly - -
  • - - } -> - -
    -`; - -exports[`should render correctly: can subscribe, subscribed 1`] = ` - -
  • - - download_verb - -
  • -
  • - - component_report.unsubscribe_x.report.frequency.weekly - -
  • - - } -> - -
    -`; - -exports[`should render correctly: cannot subscribe 1`] = ` - - component_report.download.qualifier.vw - -`; - -exports[`should render correctly: current user without email 1`] = ` - -
  • - - download_verb - -
  • -
  • - - component_report.no_email_to_subscribe - -
  • - - } -> - -
    -`; - -exports[`should render correctly: not a portfolio 1`] = ` - -
  • - - download_verb - -
  • -
  • - - component_report.subscribe_x.report.frequency.weekly - -
  • - - } -> - -
    -`; -- 2.39.5