]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18421 Migrate project nav tests to RTL
authorJeremy Davis <jeremy.davis@sonarsource.com>
Wed, 29 Mar 2023 16:38:51 +0000 (18:38 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 30 Mar 2023 20:03:07 +0000 (20:03 +0000)
24 files changed:
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavProjectBindingErrorNotif-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavProjectBindingErrorNotif-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/BadgeParams.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeButton-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeParams-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/ProjectBadges-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaLinks.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/MetaTags.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaLink-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaQualityProfiles-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTags-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaSize-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/ProjectNotifications-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/helpers/testMocks.ts

index eb2686b7ffbf71f90306eacf7f9eec2b7f5fbeb2..f4d5ad90e94ed0f95ad11364fdc51e95c43bc97d 100644 (file)
@@ -30,7 +30,7 @@ export interface ComponentNavProjectBindingErrorNotifProps {
   component: Component;
 }
 
-export function ComponentNavProjectBindingErrorNotif(
+export default function ComponentNavProjectBindingErrorNotif(
   props: ComponentNavProjectBindingErrorNotifProps
 ) {
   const { component } = props;
@@ -56,5 +56,3 @@ export function ComponentNavProjectBindingErrorNotif(
     </Alert>
   );
 }
-
-export default ComponentNavProjectBindingErrorNotif;
index e099236b8efe48f6099ff69ff25a23b391eb746b..2ec27b6f16abf994081c760069f5073c83c9b00a 100644 (file)
  * 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 * as React from 'react';
 import { mockComponent } from '../../../../../helpers/mocks/component';
-import {
-  ComponentNavProjectBindingErrorNotif,
+import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
+import ComponentNavProjectBindingErrorNotif, {
   ComponentNavProjectBindingErrorNotifProps,
 } from '../ComponentNavProjectBindingErrorNotif';
 
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('non-project admin');
+it('should not show a link if use is not allowed', () => {
+  renderComponentNavProjectBindingErrorNotif({
+    component: mockComponent({ configuration: { showSettings: false } }),
+  });
   expect(
-    shallowRender({ component: mockComponent({ configuration: { showSettings: true } }) })
-  ).toMatchSnapshot('project admin');
+    screen.queryByRole('link', {
+      name: 'component_navigation.pr_deco.action.check_project_settings',
+    })
+  ).not.toBeInTheDocument();
 });
 
-function shallowRender(props: Partial<ComponentNavProjectBindingErrorNotifProps> = {}) {
-  return shallow<ComponentNavProjectBindingErrorNotifProps>(
+it('should show a link if use is allowed', () => {
+  renderComponentNavProjectBindingErrorNotif({
+    component: mockComponent({ configuration: { showSettings: true } }),
+  });
+  expect(
+    screen.getByRole('link', { name: 'component_navigation.pr_deco.action.check_project_settings' })
+  ).toBeInTheDocument();
+});
+
+function renderComponentNavProjectBindingErrorNotif(
+  props: Partial<ComponentNavProjectBindingErrorNotifProps> = {}
+) {
+  return renderComponent(
     <ComponentNavProjectBindingErrorNotif component={mockComponent()} {...props} />
   );
 }
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavProjectBindingErrorNotif-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavProjectBindingErrorNotif-test.tsx.snap
deleted file mode 100644 (file)
index e2012ed..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: non-project admin 1`] = `
-<Alert
-  display="banner"
-  variant="warning"
->
-  <FormattedMessage
-    defaultMessage="component_navigation.pr_deco.error_detected_X"
-    id="component_navigation.pr_deco.error_detected_X"
-    values={
-      {
-        "action": "component_navigation.pr_deco.action.contact_project_admin",
-      }
-    }
-  />
-</Alert>
-`;
-
-exports[`should render correctly: project admin 1`] = `
-<Alert
-  display="banner"
-  variant="warning"
->
-  <FormattedMessage
-    defaultMessage="component_navigation.pr_deco.error_detected_X"
-    id="component_navigation.pr_deco.error_detected_X"
-    values={
-      {
-        "action": <ForwardRef(Link)
-          to={
-            {
-              "pathname": "/project/settings",
-              "search": "?id=my-project&category=pull_request_decoration_binding",
-            }
-          }
-        >
-          component_navigation.pr_deco.action.check_project_settings
-        </ForwardRef(Link)>,
-      }
-    }
-  />
-</Alert>
-`;
index 93b5ad8382dac55b73e8b8019754fc2455a7d50b..90d34112b599675779a4f81a65d45a8d4209a61a 100644 (file)
@@ -110,7 +110,7 @@ export class BadgeParams extends React.PureComponent<Props> {
           </label>
           <Select
             className="input-medium it__metric-badge-select"
-            name="badge-metric"
+            inputId="badge-metric"
             isSearchable={false}
             onChange={this.handleMetricChange}
             options={metricOptions}
@@ -139,7 +139,7 @@ export class BadgeParams extends React.PureComponent<Props> {
         </label>
         <Select
           className="input-medium"
-          name="badge-format"
+          inputId="badge-format"
           isSearchable={false}
           onChange={this.handleFormatChange}
           options={formatOptions}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeButton-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeButton-test.tsx
deleted file mode 100644 (file)
index df104bc..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * 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 * as React from 'react';
-import { click } from '../../../../../../../helpers/testUtils';
-import BadgeButton from '../BadgeButton';
-import { BadgeType } from '../utils';
-
-it('should display correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-  expect(getWrapper({ selected: true })).toMatchSnapshot();
-  expect(getWrapper({ type: BadgeType.measure })).toMatchSnapshot();
-});
-
-it('should return the badge type on click', () => {
-  const onClick = jest.fn();
-  const wrapper = getWrapper({ onClick });
-  click(wrapper.find('Button'));
-  expect(onClick).toHaveBeenCalledWith(BadgeType.qualityGate);
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <BadgeButton
-      onClick={jest.fn()}
-      selected={false}
-      type={BadgeType.qualityGate}
-      url="http://foo.bar"
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeParams-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/BadgeParams-test.tsx
deleted file mode 100644 (file)
index 3f66f3c..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * 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 * as React from 'react';
-import { Metric } from '../../../../../../../types/types';
-import { BadgeParams } from '../BadgeParams';
-import { BadgeType } from '../utils';
-
-jest.mock('../../../../../../../api/web-api', () => ({
-  fetchWebApi: () =>
-    Promise.resolve([
-      {
-        path: 'api/project_badges',
-        actions: [
-          {
-            key: 'measure',
-            params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage'] }],
-          },
-        ],
-      },
-    ]),
-}));
-
-const METRICS = {
-  alert_status: { key: 'alert_status', name: 'Quality Gate' } as Metric,
-  coverage: { key: 'coverage', name: 'Coverage' } as Metric,
-};
-
-it('should display measure badge params', () => {
-  const updateOptions = jest.fn();
-  const wrapper = getWrapper({ updateOptions, type: BadgeType.measure });
-  expect(wrapper).toMatchSnapshot();
-  (wrapper.instance() as BadgeParams).handleMetricChange({ value: 'code_smell' });
-  expect(updateOptions).toHaveBeenCalledWith({ metric: 'code_smell' });
-});
-
-it('should display quality gate badge params', () => {
-  const updateOptions = jest.fn();
-  const wrapper = getWrapper({ updateOptions, type: BadgeType.qualityGate });
-  expect(wrapper).toMatchSnapshot();
-  (wrapper.instance() as BadgeParams).handleFormatChange({ value: 'md' });
-  expect(updateOptions).toHaveBeenCalledWith({ format: 'md' });
-});
-
-function getWrapper(props = {}) {
-  return shallow(
-    <BadgeParams
-      metrics={METRICS}
-      options={{ metric: 'alert_status' }}
-      type={BadgeType.measure}
-      updateOptions={jest.fn()}
-      {...props}
-    />
-  );
-}
index eec3412b0092b0eee3e7df6f08eb122cd1df68a4..0ce8130e2f994301ea13bbe9f79100c6f1cce210 100644 (file)
  * 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 selectEvent from 'react-select-event';
 import { getProjectBadgesToken } from '../../../../../../../api/project-badges';
-import CodeSnippet from '../../../../../../../components/common/CodeSnippet';
 import { mockBranch } from '../../../../../../../helpers/mocks/branch-like';
 import { mockComponent } from '../../../../../../../helpers/mocks/component';
-import { waitAndUpdate } from '../../../../../../../helpers/testUtils';
+import { renderComponent } from '../../../../../../../helpers/testReactTestingUtils';
 import { Location } from '../../../../../../../helpers/urls';
 import { ComponentQualifier } from '../../../../../../../types/component';
-import BadgeButton from '../BadgeButton';
+import { MetricKey } from '../../../../../../../types/metrics';
 import ProjectBadges from '../ProjectBadges';
+import { BadgeType } from '../utils';
 
 jest.mock('../../../../../../../helpers/urls', () => ({
   getHostUrl: () => 'host',
@@ -40,51 +42,90 @@ jest.mock('../../../../../../../api/project-badges', () => ({
   renewProjectBadgesToken: jest.fn().mockResolvedValue({}),
 }));
 
-jest.mock('react', () => {
-  return {
-    ...jest.requireActual('react'),
-    createRef: jest.fn().mockReturnValue({ current: document.createElement('h3') }),
-  };
-});
-
-it('should display correctly', async () => {
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-});
+jest.mock('../../../../../../../api/web-api', () => ({
+  fetchWebApi: () =>
+    Promise.resolve([
+      {
+        path: 'api/project_badges',
+        actions: [
+          {
+            key: 'measure',
+            // eslint-disable-next-line local-rules/use-metrickey-enum
+            params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage'] }],
+          },
+        ],
+      },
+    ]),
+}));
 
 it('should renew token', async () => {
-  (getProjectBadgesToken as jest.Mock).mockResolvedValueOnce('foo').mockResolvedValueOnce('bar');
-  const wrapper = shallowRender({
+  const user = userEvent.setup();
+  jest.mocked(getProjectBadgesToken).mockResolvedValueOnce('foo').mockResolvedValueOnce('bar');
+  renderProjectBadges({
     component: mockComponent({ configuration: { showSettings: true } }),
   });
-  await waitAndUpdate(wrapper);
-  wrapper.find('.it__project-info-renew-badge').simulate('click');
 
-  // it shoud be loading
-  expect(wrapper.find('.it__project-info-renew-badge').props().disabled).toBe(true);
+  expect(
+    await screen.findByText(`overview.badges.get_badge.${ComponentQualifier.Project}`)
+  ).toHaveFocus();
+
+  expect(screen.getByAltText(`overview.badges.${BadgeType.qualityGate}.alt`)).toHaveAttribute(
+    'src',
+    'host/api/project_badges/quality_gate?branch=branch-6.7&project=my-project&token=foo'
+  );
+
+  expect(screen.getByAltText(`overview.badges.${BadgeType.measure}.alt`)).toHaveAttribute(
+    'src',
+    'host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=alert_status&token=foo'
+  );
+
+  await user.click(screen.getByText('overview.badges.renew'));
 
-  await waitAndUpdate(wrapper);
-  const buttons = wrapper.find(BadgeButton);
-  expect(buttons.at(0).props().url).toMatch('token=bar');
-  expect(buttons.at(1).props().url).toMatch('token=bar');
-  expect(wrapper.find(CodeSnippet).props().snippet).toMatch('token=bar');
+  expect(
+    await screen.findByAltText(`overview.badges.${BadgeType.qualityGate}.alt`)
+  ).toHaveAttribute(
+    'src',
+    'host/api/project_badges/quality_gate?branch=branch-6.7&project=my-project&token=bar'
+  );
 
-  // let's check that the loading has correclty ends.
-  expect(wrapper.find('.it__project-info-renew-badge').props().disabled).toBe(false);
+  expect(screen.getByAltText(`overview.badges.${BadgeType.measure}.alt`)).toHaveAttribute(
+    'src',
+    'host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=alert_status&token=bar'
+  );
 });
 
-it('should set focus on the heading when rendered', async () => {
-  const fakeElement = document.createElement('h3');
-  const focus = jest.fn();
-  (React.createRef as jest.Mock).mockReturnValueOnce({ current: { ...fakeElement, focus } });
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-  expect(focus).toHaveBeenCalled();
+it('should update params', async () => {
+  renderProjectBadges({
+    component: mockComponent({ configuration: { showSettings: true } }),
+  });
+
+  expect(
+    await screen.findByText(
+      '[![alert_status](host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=alert_status&token=foo)](/dashboard)'
+    )
+  ).toBeInTheDocument();
+
+  await selectEvent.select(screen.getByLabelText('format:'), [
+    'overview.badges.options.formats.url',
+  ]);
+
+  expect(
+    screen.getByText(
+      'host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=alert_status&token=foo'
+    )
+  ).toBeInTheDocument();
+
+  await selectEvent.select(screen.getByLabelText('overview.badges.metric:'), MetricKey.coverage);
+
+  expect(
+    screen.getByText(
+      `host/api/project_badges/measure?branch=branch-6.7&project=my-project&metric=${MetricKey.coverage}&token=foo`
+    )
+  ).toBeInTheDocument();
 });
 
-function shallowRender(props: Partial<ProjectBadges['props']> = {}) {
-  return shallow<ProjectBadges>(
+function renderProjectBadges(props: Partial<ProjectBadges['props']> = {}) {
+  return renderComponent(
     <ProjectBadges
       branchLike={mockBranch()}
       component={mockComponent({ key: 'foo', qualifier: ComponentQualifier.Project })}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeButton-test.tsx.snap
deleted file mode 100644 (file)
index b30b2a0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<Button
-  className="badge-button"
-  onClick={[Function]}
->
-  <img
-    alt="overview.badges.quality_gate.alt"
-    src="http://foo.bar"
-    width="128px"
-  />
-</Button>
-`;
-
-exports[`should display correctly 2`] = `
-<Button
-  className="badge-button selected"
-  onClick={[Function]}
->
-  <img
-    alt="overview.badges.quality_gate.alt"
-    src="http://foo.bar"
-    width="128px"
-  />
-</Button>
-`;
-
-exports[`should display correctly 3`] = `
-<Button
-  className="badge-button"
-  onClick={[Function]}
->
-  <img
-    alt="overview.badges.measure.alt"
-    src="http://foo.bar"
-  />
-</Button>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/BadgeParams-test.tsx.snap
deleted file mode 100644 (file)
index fcaba89..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display measure badge params 1`] = `
-<div>
-  <label
-    className="spacer-right"
-    htmlFor="badge-metric"
-  >
-    overview.badges.metric
-    :
-  </label>
-  <Select
-    className="input-medium it__metric-badge-select"
-    isSearchable={false}
-    name="badge-metric"
-    onChange={[Function]}
-    options={[]}
-  />
-  <label
-    className="spacer-right spacer-top"
-    htmlFor="badge-format"
-  >
-    format
-    :
-  </label>
-  <Select
-    className="input-medium"
-    defaultValue={
-      {
-        "label": "overview.badges.options.formats.md",
-        "value": "md",
-      }
-    }
-    isSearchable={false}
-    name="badge-format"
-    onChange={[Function]}
-    options={
-      [
-        {
-          "label": "overview.badges.options.formats.md",
-          "value": "md",
-        },
-        {
-          "label": "overview.badges.options.formats.url",
-          "value": "url",
-        },
-      ]
-    }
-  />
-</div>
-`;
-
-exports[`should display quality gate badge params 1`] = `
-<div>
-  <label
-    className="spacer-right"
-    htmlFor="badge-format"
-  >
-    format
-    :
-  </label>
-  <Select
-    className="input-medium"
-    defaultValue={
-      {
-        "label": "overview.badges.options.formats.md",
-        "value": "md",
-      }
-    }
-    isSearchable={false}
-    name="badge-format"
-    onChange={[Function]}
-    options={
-      [
-        {
-          "label": "overview.badges.options.formats.md",
-          "value": "md",
-        },
-        {
-          "label": "overview.badges.options.formats.url",
-          "value": "url",
-        },
-      ]
-    }
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/badges/__tests__/__snapshots__/ProjectBadges-test.tsx.snap
deleted file mode 100644 (file)
index 402bd1f..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<div
-  className="display-flex-column"
->
-  <h3
-    tabIndex={-1}
-  >
-    overview.badges.get_badge.TRK
-  </h3>
-  <p
-    className="big-spacer-bottom"
-  >
-    overview.badges.description.TRK
-  </p>
-  <BadgeButton
-    onClick={[Function]}
-    selected={true}
-    type="measure"
-    url="host/api/project_badges/measure?branch=branch-6.7&project=foo&metric=alert_status&token=foo"
-  />
-  <p
-    className="huge-spacer-bottom spacer-top"
-  >
-    overview.badges.measure.description.TRK
-  </p>
-  <BadgeButton
-    onClick={[Function]}
-    selected={false}
-    type="quality_gate"
-    url="host/api/project_badges/quality_gate?branch=branch-6.7&project=foo&token=foo"
-  />
-  <p
-    className="huge-spacer-bottom spacer-top"
-  >
-    overview.badges.quality_gate.description.TRK
-  </p>
-  <withMetricsContext(BadgeParams)
-    className="big-spacer-bottom display-flex-column"
-    options={
-      {
-        "metric": "alert_status",
-      }
-    }
-    type="measure"
-    updateOptions={[Function]}
-  />
-  <CodeSnippet
-    isOneLine={true}
-    snippet="[![alert_status](host/api/project_badges/measure?branch=branch-6.7&project=foo&metric=alert_status&token=foo)](/dashboard)"
-  />
-  <Alert
-    variant="warning"
-  >
-    <p>
-      overview.badges.leak_warning
-       
-    </p>
-  </Alert>
-</div>
-`;
index bfb82087c2596c6cfa34d30b80a504e89b77a7c7..8976d94d0142efe12b67d53fba93e7439debd330 100644 (file)
@@ -71,16 +71,14 @@ export default class MetaLinks extends React.PureComponent<Props, State> {
     const orderedLinks = orderLinks(links);
 
     return (
-      <>
-        <div className="big-padded bordered-bottom">
-          <h3>{translate('overview.external_links')}</h3>
-          <ul className="project-info-list">
-            {orderedLinks.map((link) => (
-              <MetaLink key={link.id} link={link} />
-            ))}
-          </ul>
-        </div>
-      </>
+      <div className="big-padded bordered-bottom">
+        <h3>{translate('overview.external_links')}</h3>
+        <ul className="project-info-list">
+          {orderedLinks.map((link) => (
+            <MetaLink key={link.id} link={link} />
+          ))}
+        </ul>
+      </div>
     );
   }
 }
index cb7325089035a66b1c28841f6113b3a295ada3a6..d5497165c3cbf3b1c53ca423cb626072328a9fe5 100644 (file)
@@ -34,20 +34,11 @@ interface Props {
 }
 
 export default class MetaTags extends React.PureComponent<Props> {
-  card?: HTMLDivElement | null;
-  tagsList?: HTMLElement | null;
-  tagsSelector?: HTMLDivElement | null;
-
   canUpdateTags = () => {
     const { configuration } = this.props.component;
     return configuration && configuration.showSettings;
   };
 
-  getPopupPos = (eltPos: ClientRect, containerPos: ClientRect) => ({
-    top: eltPos.height,
-    right: containerPos.width - eltPos.width,
-  });
-
   setTags = (values: string[]) => {
     const { component } = this.props;
 
@@ -56,12 +47,12 @@ export default class MetaTags extends React.PureComponent<Props> {
         application: component.key,
         tags: values.join(','),
       });
-    } else {
-      return setProjectTags({
-        project: component.key,
-        tags: values.join(','),
-      });
     }
+
+    return setProjectTags({
+      project: component.key,
+      tags: values.join(','),
+    });
   };
 
   handleSetProjectTags = (values: string[]) => {
@@ -74,33 +65,29 @@ export default class MetaTags extends React.PureComponent<Props> {
   render() {
     const tags = this.props.component.tags || [];
 
-    if (this.canUpdateTags()) {
-      return (
-        <div className="big-spacer-top project-info-tags" ref={(card) => (this.card = card)}>
-          <Dropdown
-            closeOnClick={false}
-            closeOnClickOutside={true}
-            overlay={
-              <MetaTagsSelector selectedTags={tags} setProjectTags={this.handleSetProjectTags} />
-            }
-            overlayPlacement={PopupPlacement.BottomLeft}
-          >
-            <ButtonLink innerRef={(tagsList) => (this.tagsList = tagsList)} stopPropagation={true}>
-              <TagsList allowUpdate={true} tags={tags.length ? tags : [translate('no_tags')]} />
-            </ButtonLink>
-          </Dropdown>
-        </div>
-      );
-    } else {
-      return (
-        <div className="big-spacer-top project-info-tags">
-          <TagsList
-            allowUpdate={false}
-            className="note"
-            tags={tags.length ? tags : [translate('no_tags')]}
-          />
-        </div>
-      );
-    }
+    return this.canUpdateTags() ? (
+      <div className="big-spacer-top project-info-tags">
+        <Dropdown
+          closeOnClick={false}
+          closeOnClickOutside={true}
+          overlay={
+            <MetaTagsSelector selectedTags={tags} setProjectTags={this.handleSetProjectTags} />
+          }
+          overlayPlacement={PopupPlacement.BottomLeft}
+        >
+          <ButtonLink stopPropagation={true}>
+            <TagsList allowUpdate={true} tags={tags.length ? tags : [translate('no_tags')]} />
+          </ButtonLink>
+        </Dropdown>
+      </div>
+    ) : (
+      <div className="big-spacer-top project-info-tags">
+        <TagsList
+          allowUpdate={false}
+          className="note"
+          tags={tags.length ? tags : [translate('no_tags')]}
+        />
+      </div>
+    );
   }
 }
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaLink-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaLink-test.tsx
deleted file mode 100644 (file)
index 80a8e11..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * 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 * as React from 'react';
-import { ClearButton } from '../../../../../../../components/controls/buttons';
-import { click } from '../../../../../../../helpers/testUtils';
-import MetaLink from '../MetaLink';
-
-const DANGEROUS_LINK = {
-  id: '1',
-  name: 'Dangerous',
-  url: 'javascript:alert("hi")',
-  type: 'dangerous',
-};
-
-it('should match snapshot', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(shallowRender({ iconOnly: true })).toMatchSnapshot('icon only');
-  const wrapper = shallowRender({ link: DANGEROUS_LINK });
-  expect(wrapper).toMatchSnapshot('dangerous link, collapsed');
-  wrapper.setState({ expanded: true });
-  expect(wrapper).toMatchSnapshot('dangerous link, expanded');
-});
-
-it('should expand and collapse dangerous links', () => {
-  const wrapper = shallowRender({ link: DANGEROUS_LINK });
-  expect(wrapper.state().expanded).toBe(false);
-
-  // expand
-  click(wrapper.find('a'));
-  expect(wrapper.state().expanded).toBe(true);
-
-  // collapse
-  click(wrapper.find('a'));
-  expect(wrapper.state().expanded).toBe(false);
-
-  // collapse with button
-  wrapper.setState({ expanded: true });
-  click(wrapper.find(ClearButton));
-  expect(wrapper.state().expanded).toBe(false);
-});
-
-function shallowRender(props: Partial<MetaLink['props']> = {}) {
-  return shallow<MetaLink>(
-    <MetaLink
-      link={{
-        id: '1',
-        name: 'Foo',
-        url: 'http://example.com',
-        type: 'foo',
-      }}
-      {...props}
-    />
-  );
-}
index 54c0ab5d29983cd311874b2daae9c3ab9f51715c..1a5cb143bcebd6e7cf41e435eae4aad4d749f939 100644 (file)
  * 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 * as React from 'react';
 import { searchRules } from '../../../../../../../api/rules';
 import { mockLanguage, mockQualityProfile } from '../../../../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../../../../helpers/testUtils';
+import { renderComponent } from '../../../../../../../helpers/testReactTestingUtils';
+import { SearchRulesResponse } from '../../../../../../../types/coding-rules';
+import { Dict } from '../../../../../../../types/types';
 import { MetaQualityProfiles } from '../MetaQualityProfiles';
 
 jest.mock('../../../../../../../api/rules', () => {
@@ -33,28 +35,39 @@ jest.mock('../../../../../../../api/rules', () => {
 });
 
 it('should render correctly', async () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
+  const totals: Dict<number> = {
+    js: 0,
+    ts: 10,
+    css: 0,
+  };
+  jest.mocked(searchRules).mockImplementation(({ qprofile }: { qprofile: string }) => {
+    return Promise.resolve({ total: totals[qprofile] } as SearchRulesResponse);
+  });
+
+  renderMetaQualityprofiles();
 
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-  expect(wrapper.find('.project-info-deprecated-rules').exists()).toBe(true);
-  expect(wrapper.find('.project-info-deleted-profile').exists()).toBe(true);
-  expect(searchRules).toHaveBeenCalled();
+  expect(await screen.findByText('overview.deleted_profile.javascript')).toBeInTheDocument();
+  expect(screen.getByText('overview.deprecated_profile.10')).toBeInTheDocument();
 });
 
-function shallowRender(props: Partial<MetaQualityProfiles['props']> = {}) {
-  return shallow(
+function renderMetaQualityprofiles(overrides: Partial<MetaQualityProfiles['props']> = {}) {
+  return renderComponent(
     <MetaQualityProfiles
       languages={{ css: mockLanguage() }}
       profiles={[
-        { ...mockQualityProfile({ key: 'js' }), deleted: true },
+        { ...mockQualityProfile({ key: 'js', name: 'javascript' }), deleted: true },
+        { ...mockQualityProfile({ key: 'ts', name: 'typescript' }), deleted: false },
         {
-          ...mockQualityProfile({ key: 'css', language: 'css', languageName: 'CSS' }),
+          ...mockQualityProfile({
+            key: 'css',
+            name: 'style',
+            language: 'css',
+            languageName: 'CSS',
+          }),
           deleted: false,
         },
       ]}
-      {...props}
+      {...overrides}
     />
   );
 }
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaSize-test.tsx
deleted file mode 100644 (file)
index d786f96..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * 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 * as React from 'react';
-import { mockComponent } from '../../../../../../../helpers/mocks/component';
-import { mockMeasure } from '../../../../../../../helpers/testMocks';
-import { ComponentQualifier } from '../../../../../../../types/component';
-import { MetricKey } from '../../../../../../../types/metrics';
-import MetaSize, { MetaSizeProps } from '../MetaSize';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('project');
-  expect(
-    shallowRender({ component: mockComponent({ qualifier: ComponentQualifier.Application }) })
-  ).toMatchSnapshot('application');
-});
-
-function shallowRender(props: Partial<MetaSizeProps> = {}) {
-  return shallow<MetaSizeProps>(
-    <MetaSize
-      component={mockComponent()}
-      measures={[
-        mockMeasure({ metric: MetricKey.ncloc }),
-        mockMeasure({ metric: MetricKey.projects }),
-      ]}
-      {...props}
-    />
-  );
-}
index d637a33d1add25193814f7cfded33d35111f7b38..c4c8804797fba5d736b005be64480364f1e6830c 100644 (file)
  * 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 { setApplicationTags, setProjectTags } from '../../../../../../../api/components';
+import {
+  searchProjectTags,
+  setApplicationTags,
+  setProjectTags,
+} from '../../../../../../../api/components';
 import { mockComponent } from '../../../../../../../helpers/mocks/component';
+import { renderComponent } from '../../../../../../../helpers/testReactTestingUtils';
 import { ComponentQualifier } from '../../../../../../../types/component';
 import MetaTags from '../MetaTags';
 
 jest.mock('../../../../../../../api/components', () => ({
   setApplicationTags: jest.fn().mockResolvedValue(true),
   setProjectTags: jest.fn().mockResolvedValue(true),
+  searchProjectTags: jest.fn(),
 }));
 
 beforeEach(() => {
   jest.clearAllMocks();
 });
 
-it('should render without tags and admin rights', () => {
-  expect(shallowRender()).toMatchSnapshot();
+it('should render without tags and admin rights', async () => {
+  renderMetaTags();
+
+  expect(await screen.findByText('no_tags')).toBeInTheDocument();
+  expect(screen.queryByRole('button')).not.toBeInTheDocument();
 });
 
-it('should render with tags and admin rights', () => {
+it('should allow to edit tags for a project', async () => {
+  const user = userEvent.setup();
+  jest.mocked(searchProjectTags).mockResolvedValue({ tags: ['best', 'useless'] });
+
+  const onComponentChange = jest.fn();
   const component = mockComponent({
     key: 'my-second-project',
     tags: ['foo', 'bar'],
@@ -47,37 +61,58 @@ it('should render with tags and admin rights', () => {
     name: 'MySecondProject',
   });
 
-  expect(shallowRender({ component })).toMatchSnapshot();
-});
+  renderMetaTags({ component, onComponentChange });
+
+  expect(await screen.findByText('foo, bar')).toBeInTheDocument();
+  expect(screen.getByRole('button')).toBeInTheDocument();
+
+  await user.click(screen.getByRole('button', { name: 'tags_list_x.foo, bar' }));
 
-it('should set tags for a project', () => {
-  const wrapper = shallowRender();
+  expect(await screen.findByText('best')).toBeInTheDocument();
 
-  wrapper.instance().handleSetProjectTags(['tag1', 'tag2']);
+  await user.click(screen.getByText('best'));
+  expect(onComponentChange).toHaveBeenCalledWith({ tags: ['foo', 'bar', 'best'] });
+
+  onComponentChange.mockClear();
+
+  /*
+   * Since we're not actually updating the tags, we're back to having the foo, bar only
+   */
+  await user.click(screen.getByText('bar'));
+  expect(onComponentChange).toHaveBeenCalledWith({ tags: ['foo'] });
 
   expect(setProjectTags).toHaveBeenCalled();
   expect(setApplicationTags).not.toHaveBeenCalled();
 });
 
-it('should set tags for an app', () => {
-  const wrapper = shallowRender({
-    component: mockComponent({ qualifier: ComponentQualifier.Application }),
+it('should set tags for an app', async () => {
+  const user = userEvent.setup();
+
+  renderMetaTags({
+    component: mockComponent({
+      configuration: {
+        showSettings: true,
+      },
+      qualifier: ComponentQualifier.Application,
+    }),
   });
 
-  wrapper.instance().handleSetProjectTags(['tag1', 'tag2']);
+  await user.click(screen.getByRole('button', { name: 'tags_list_x.no_tags' }));
+
+  await user.click(screen.getByText('best'));
 
   expect(setProjectTags).not.toHaveBeenCalled();
   expect(setApplicationTags).toHaveBeenCalled();
 });
 
-function shallowRender(overrides: Partial<MetaTags['props']> = {}) {
+function renderMetaTags(overrides: Partial<MetaTags['props']> = {}) {
   const component = mockComponent({
     configuration: {
       showSettings: false,
     },
   });
 
-  return shallow<MetaTags>(
+  return renderComponent(
     <MetaTags component={component} onComponentChange={jest.fn()} {...overrides} />
   );
 }
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/MetaTagsSelector-test.tsx
deleted file mode 100644 (file)
index 7761f1b..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-/* eslint-disable import/first */
-import { mount, shallow } from 'enzyme';
-import * as React from 'react';
-import { searchProjectTags } from '../../../../../../../api/components';
-import MetaTagsSelector from '../MetaTagsSelector';
-
-jest.mock('../../../../../../../api/components', () => ({
-  searchProjectTags: jest.fn(),
-}));
-
-jest.mock('lodash', () => {
-  const lodash = jest.requireActual('lodash');
-  lodash.debounce = jest.fn((fn) => fn);
-  return lodash;
-});
-
-it('searches tags on mount', () => {
-  (searchProjectTags as jest.Mock).mockImplementation(() =>
-    Promise.resolve({ tags: ['foo', 'bar'] })
-  );
-  mount(<MetaTagsSelector selectedTags={[]} setProjectTags={jest.fn()} />);
-  expect(searchProjectTags).toHaveBeenCalledWith({ ps: 9, q: '' });
-});
-
-it('selects and deselects tags', () => {
-  const setProjectTags = jest.fn();
-  const wrapper = shallow(
-    <MetaTagsSelector selectedTags={['foo', 'bar']} setProjectTags={setProjectTags} />
-  );
-
-  const tagSelect: any = wrapper.find('TagsSelector');
-  tagSelect.prop('onSelect')('baz');
-  expect(setProjectTags).toHaveBeenLastCalledWith(['foo', 'bar', 'baz']);
-
-  // note that the `selectedTags` is a prop and so it wasn't changed
-  tagSelect.prop('onUnselect')('bar');
-  expect(setProjectTags).toHaveBeenLastCalledWith(['foo']);
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaLink-test.tsx.snap
deleted file mode 100644 (file)
index 8047eda..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should match snapshot: dangerous link, collapsed 1`] = `
-<li>
-  <a
-    className="link-no-underline"
-    onClick={[Function]}
-    rel="nofollow noreferrer noopener"
-    target="_blank"
-    title="Dangerous"
-  >
-    <ProjectLinkIcon
-      className="little-spacer-right"
-      type="dangerous"
-    />
-    Dangerous
-  </a>
-</li>
-`;
-
-exports[`should match snapshot: dangerous link, expanded 1`] = `
-<li>
-  <a
-    className="link-no-underline"
-    onClick={[Function]}
-    rel="nofollow noreferrer noopener"
-    target="_blank"
-    title="Dangerous"
-  >
-    <ProjectLinkIcon
-      className="little-spacer-right"
-      type="dangerous"
-    />
-    Dangerous
-  </a>
-  <div
-    className="little-spacer-top display-flex-center"
-  >
-    <input
-      className="overview-key width-80"
-      onClick={[Function]}
-      readOnly={true}
-      type="text"
-      value="javascript:alert("hi")"
-    />
-    <ClearButton
-      className="little-spacer-left"
-      onClick={[Function]}
-    />
-  </div>
-</li>
-`;
-
-exports[`should match snapshot: default 1`] = `
-<li>
-  <a
-    className="link-no-underline"
-    href="http://example.com"
-    rel="nofollow noreferrer noopener"
-    target="_blank"
-    title="Foo"
-  >
-    <ProjectLinkIcon
-      className="little-spacer-right"
-      type="foo"
-    />
-    Foo
-  </a>
-</li>
-`;
-
-exports[`should match snapshot: icon only 1`] = `
-<li>
-  <a
-    className="link-no-underline"
-    href="http://example.com"
-    rel="nofollow noreferrer noopener"
-    target="_blank"
-    title="Foo"
-  >
-    <ProjectLinkIcon
-      className="little-spacer-right"
-      type="foo"
-    />
-  </a>
-</li>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaQualityProfiles-test.tsx.snap
deleted file mode 100644 (file)
index 7ebb9e6..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <h3>
-    overview.quality_profiles
-  </h3>
-  <ul
-    className="project-info-list"
-  >
-    <Tooltip
-      key="js"
-      overlay="overview.deleted_profile.name"
-    >
-      <li
-        className="project-info-deleted-profile"
-      >
-        <div
-          className="text-ellipsis"
-        >
-          <span
-            className="spacer-right"
-          >
-            (
-            js
-            )
-          </span>
-          name
-        </div>
-      </li>
-    </Tooltip>
-    <li
-      key="css"
-    >
-      <div
-        className="text-ellipsis"
-      >
-        <span
-          className="spacer-right"
-        >
-          (
-          CSS
-          )
-        </span>
-        <ForwardRef(Link)
-          to={
-            {
-              "pathname": "/profiles/show",
-              "search": "?name=name&language=css",
-            }
-          }
-        >
-          <span
-            aria-label="overview.link_to_x_profile_y.CSS.name"
-          >
-            name
-          </span>
-        </ForwardRef(Link)>
-      </div>
-    </li>
-  </ul>
-</Fragment>
-`;
-
-exports[`should render correctly 2`] = `
-<Fragment>
-  <h3>
-    overview.quality_profiles
-  </h3>
-  <ul
-    className="project-info-list"
-  >
-    <Tooltip
-      key="js"
-      overlay="overview.deleted_profile.name"
-    >
-      <li
-        className="project-info-deleted-profile"
-      >
-        <div
-          className="text-ellipsis"
-        >
-          <span
-            className="spacer-right"
-          >
-            (
-            js
-            )
-          </span>
-          name
-        </div>
-      </li>
-    </Tooltip>
-    <Tooltip
-      key="css"
-      overlay="overview.deprecated_profile.10"
-    >
-      <li
-        className="project-info-deprecated-rules"
-      >
-        <div
-          className="text-ellipsis"
-        >
-          <span
-            className="spacer-right"
-          >
-            (
-            CSS
-            )
-          </span>
-          <ForwardRef(Link)
-            to={
-              {
-                "pathname": "/profiles/show",
-                "search": "?name=name&language=css",
-              }
-            }
-          >
-            <span
-              aria-label="overview.link_to_x_profile_y.CSS.name"
-            >
-              name
-            </span>
-          </ForwardRef(Link)>
-        </div>
-      </li>
-    </Tooltip>
-  </ul>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaSize-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaSize-test.tsx.snap
deleted file mode 100644 (file)
index c9cbdc4..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: application 1`] = `
-<Fragment>
-  <div
-    className="display-flex-row display-inline-flex-baseline"
-  >
-    <h3>
-      metric.ncloc.name
-    </h3>
-    <span
-      className="spacer-left small"
-    >
-      (
-      project.info.main_branch
-      )
-    </span>
-  </div>
-  <div
-    className="display-flex-center"
-  >
-    <DrilldownLink
-      className="huge"
-      component="my-project"
-      metric="ncloc"
-    >
-      <span
-        aria-label="project.info.see_more_info_on_x_locs.1.0"
-      >
-        1
-      </span>
-    </DrilldownLink>
-    <span
-      className="spacer-left"
-    >
-      <SizeRating
-        value={1}
-      />
-    </span>
-    <span
-      className="huge-spacer-left display-inline-flex-center"
-    >
-      <DrilldownLink
-        component="my-project"
-        metric="projects"
-      >
-        <span
-          className="big"
-        >
-          1
-        </span>
-      </DrilldownLink>
-      <span
-        className="little-spacer-left text-muted"
-      >
-        metric.projects.name
-      </span>
-    </span>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly: project 1`] = `
-<Fragment>
-  <div
-    className="display-flex-row display-inline-flex-baseline"
-  >
-    <h3>
-      metric.ncloc.name
-    </h3>
-    <span
-      className="spacer-left small"
-    >
-      (
-      project.info.main_branch
-      )
-    </span>
-  </div>
-  <div
-    className="display-flex-center"
-  >
-    <DrilldownLink
-      className="huge"
-      component="my-project"
-      metric="ncloc"
-    >
-      <span
-        aria-label="project.info.see_more_info_on_x_locs.1.0"
-      >
-        1
-      </span>
-    </DrilldownLink>
-    <span
-      className="spacer-left"
-    >
-      <SizeRating
-        value={1}
-      />
-    </span>
-  </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/meta/__tests__/__snapshots__/MetaTags-test.tsx.snap
deleted file mode 100644 (file)
index b13505d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render with tags and admin rights 1`] = `
-<div
-  className="big-spacer-top project-info-tags"
->
-  <Dropdown
-    closeOnClick={false}
-    closeOnClickOutside={true}
-    overlay={
-      <MetaTagsSelector
-        selectedTags={
-          [
-            "foo",
-            "bar",
-          ]
-        }
-        setProjectTags={[Function]}
-      />
-    }
-    overlayPlacement="bottom-left"
-  >
-    <ButtonLink
-      innerRef={[Function]}
-      stopPropagation={true}
-    >
-      <TagsList
-        allowUpdate={true}
-        tags={
-          [
-            "foo",
-            "bar",
-          ]
-        }
-      />
-    </ButtonLink>
-  </Dropdown>
-</div>
-`;
-
-exports[`should render without tags and admin rights 1`] = `
-<div
-  className="big-spacer-top project-info-tags"
->
-  <TagsList
-    allowUpdate={false}
-    className="note"
-    tags={
-      [
-        "no_tags",
-      ]
-    }
-  />
-</div>
-`;
index f2ecd8cfffb78da345d526d64f70a96eee1b8c75..a9950202ad43e187326a4412850b54533acc20c0 100644 (file)
  * 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 { getNotifications } from '../../../../../../../api/notifications';
 import { mockComponent } from '../../../../../../../helpers/mocks/component';
+import { mockNotification } from '../../../../../../../helpers/testMocks';
+import { renderComponent } from '../../../../../../../helpers/testReactTestingUtils';
 import {
   NotificationGlobalType,
   NotificationProjectType,
 } from '../../../../../../../types/notifications';
-import { ProjectNotifications } from '../ProjectNotifications';
+import ProjectNotifications from '../ProjectNotifications';
 
-jest.mock('react', () => {
-  return {
-    ...jest.requireActual('react'),
-    useEffect: jest.fn().mockImplementation((f) => f()),
-    useRef: jest.fn().mockReturnValue({ current: document.createElement('h3') }),
-  };
-});
+jest.mock('../../../../../../../api/notifications', () => ({
+  addNotification: jest.fn().mockResolvedValue(undefined),
+  removeNotification: jest.fn().mockResolvedValue(undefined),
+  getNotifications: jest.fn(),
+}));
 
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
+beforeAll(() => {
+  jest.mocked(getNotifications).mockResolvedValue({
+    channels: ['channel1'],
+    globalTypes: [NotificationGlobalType.MyNewIssues],
+    notifications: [
+      mockNotification({}),
+      mockNotification({ type: NotificationProjectType.NewAlerts }),
+    ],
+    perProjectTypes: [NotificationProjectType.NewAlerts, NotificationProjectType.NewIssues],
+  });
 });
 
-it('should add and remove a notification for the project', () => {
-  const addNotification = jest.fn();
-  const removeNotification = jest.fn();
-  const wrapper = shallowRender({ addNotification, removeNotification });
-  const notification = {
-    channel: 'EmailNotificationChannel',
-    type: 'SQ-MyNewIssues',
-  };
+it('should render correctly', async () => {
+  const user = userEvent.setup();
+  renderProjectNotifications();
 
-  wrapper.find('NotificationsList').prop<Function>('onAdd')(notification);
-  expect(addNotification).toHaveBeenCalledWith({ ...notification, project: 'foo' });
+  expect(await screen.findByText('notification.channel.channel1')).toBeInTheDocument();
+  expect(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewAlerts.project'
+    )
+  ).toBeChecked();
 
-  wrapper.find('NotificationsList').prop<Function>('onRemove')(notification);
-  expect(removeNotification).toHaveBeenCalledWith({ ...notification, project: 'foo' });
-});
+  expect(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewIssues.project'
+    )
+  ).not.toBeChecked();
+
+  // Toggle New Alerts
+  await user.click(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewAlerts.project'
+    )
+  );
+
+  expect(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewAlerts.project'
+    )
+  ).not.toBeChecked();
 
-it('should set focus on the heading when rendered', () => {
-  const fakeElement = document.createElement('h3');
-  const focus = jest.fn();
-  (React.useRef as jest.Mock).mockReturnValueOnce({ current: { ...fakeElement, focus } });
+  // Toggle New Issues
+  await user.click(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewIssues.project'
+    )
+  );
 
-  shallowRender();
-  expect(focus).toHaveBeenCalled();
+  expect(
+    screen.getByLabelText(
+      'notification.dispatcher.descrption_x.notification.dispatcher.NewIssues.project'
+    )
+  ).toBeChecked();
 });
 
-function shallowRender(props = {}) {
-  return shallow(
-    <ProjectNotifications
-      addNotification={jest.fn()}
-      channels={['channel1', 'channel2']}
-      component={mockComponent({ key: 'foo' })}
-      globalTypes={[NotificationGlobalType.CeReportTaskFailure, NotificationGlobalType.NewAlerts]}
-      loading={false}
-      notifications={[
-        {
-          channel: 'channel1',
-          type: 'type-global',
-          project: 'foo',
-          projectName: 'Foo',
-        },
-        {
-          channel: 'channel1',
-          type: 'type-common',
-          project: 'bar',
-          projectName: 'Bar',
-        },
-        {
-          channel: 'channel2',
-          type: 'type-common',
-          project: 'qux',
-          projectName: 'Qux',
-        },
-      ]}
-      perProjectTypes={[NotificationProjectType.CeReportTaskFailure]}
-      removeNotification={jest.fn()}
-      {...props}
-    />
+function renderProjectNotifications() {
+  return renderComponent(
+    <ProjectNotifications component={mockComponent({ key: 'foo', name: 'Foo' })} />
   );
 }
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/projectInformation/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap
deleted file mode 100644 (file)
index b36d5d8..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <h3
-    tabIndex={-1}
-  >
-    project.info.notifications
-  </h3>
-  <Alert
-    aria-live="off"
-    className="spacer-top"
-    variant="info"
-  >
-    notification.dispatcher.information
-  </Alert>
-  <DeferredSpinner
-    loading={false}
-  >
-    <table
-      className="data zebra notifications-table"
-    >
-      <thead>
-        <tr>
-          <th
-            aria-label="project"
-          />
-          <th
-            className="text-center"
-            key="channel1"
-          >
-            <h4>
-              notification.channel.channel1
-            </h4>
-          </th>
-          <th
-            className="text-center"
-            key="channel2"
-          >
-            <h4>
-              notification.channel.channel2
-            </h4>
-          </th>
-        </tr>
-      </thead>
-      <NotificationsList
-        channels={
-          [
-            "channel1",
-            "channel2",
-          ]
-        }
-        checkboxId={[Function]}
-        notifications={
-          [
-            {
-              "channel": "channel1",
-              "project": "foo",
-              "projectName": "Foo",
-              "type": "type-global",
-            },
-          ]
-        }
-        onAdd={[Function]}
-        onRemove={[Function]}
-        project={true}
-        types={
-          [
-            "CeReportTaskFailure",
-          ]
-        }
-      />
-    </table>
-  </DeferredSpinner>
-</Fragment>
-`;
index 758edd11a4d5a311441f58cd5a4f811b14cb49fa..b2824755591cc7753aa37f230e8d5024d93a76b3 100644 (file)
@@ -27,6 +27,7 @@ import { RuleRepository } from '../types/coding-rules';
 import { EditionKey } from '../types/editions';
 import { IssueScope, IssueSeverity, IssueStatus, IssueType, RawIssue } from '../types/issues';
 import { Language } from '../types/languages';
+import { Notification } from '../types/notifications';
 import { DumpStatus, DumpTask } from '../types/project-dump';
 import { TaskStatuses } from '../types/tasks';
 import {
@@ -414,6 +415,16 @@ export function mockMeasureEnhanced(overrides: Partial<MeasureEnhanced> = {}): M
   };
 }
 
+export function mockNotification(overrides: Partial<Notification> = {}): Notification {
+  return {
+    channel: 'channel1',
+    type: 'type-global',
+    project: 'foo',
+    projectName: 'Foo',
+    ...overrides,
+  };
+}
+
 export function mockPeriod(overrides: Partial<Period> = {}): Period {
   return {
     date: '2019-04-23T02:12:32+0100',