]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12284 Remove call to /api/components/show to verify access
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Fri, 12 Jul 2019 15:51:03 +0000 (17:51 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 18 Jul 2019 18:21:11 +0000 (20:21 +0200)
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap

index 4e99b7f715b1e43a40b3df6d2abdeb0084f21ca1..b76fe3fa4ab20cc4e379523c9aeff1630ed116e3 100644 (file)
@@ -43,7 +43,7 @@ export default class ProjectRow extends React.PureComponent<Props> {
     const { organization, project, selected } = this.props;
 
     return (
-      <tr>
+      <tr data-project-key={project.key}>
         <td className="thin">
           <Checkbox checked={selected} onCheck={this.handleProjectCheck} />
         </td>
index a2578797b24bc96e643ed79f7955577ed54a3363..54727d18669c99c22b072efb8996eead3e1ae8fa 100644 (file)
@@ -22,9 +22,10 @@ import { translate } from 'sonar-ui-common/helpers/l10n';
 import ActionsDropdown, {
   ActionsDropdownItem
 } from 'sonar-ui-common/components/controls/ActionsDropdown';
+import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
 import RestoreAccessModal from './RestoreAccessModal';
 import ApplyTemplate from '../permissions/project/components/ApplyTemplate';
-import { getComponentShow, Project } from '../../api/components';
+import { Project } from '../../api/components';
 import { getComponentNavigation } from '../../api/nav';
 import { getComponentPermissionsUrl } from '../../helpers/urls';
 
@@ -54,24 +55,17 @@ export default class ProjectRowActions extends React.PureComponent<Props, State>
   }
 
   fetchPermissions = () => {
-    this.setState({ loading: false });
-    // call `getComponentNavigation` to check if user has the "Administer" permission
-    // call `getComponentShow` to check if user has the "Browse" permission
-    Promise.all([
-      getComponentNavigation({ component: this.props.project.key }),
-      getComponentShow({ component: this.props.project.key })
-    ]).then(
-      ([navResponse]) => {
+    this.setState({ loading: true });
+    getComponentNavigation({ component: this.props.project.key }).then(
+      ({ configuration }) => {
         if (this.mounted) {
-          const hasAccess = Boolean(
-            navResponse.configuration && navResponse.configuration.showPermissions
-          );
+          const hasAccess = Boolean(configuration && configuration.showPermissions);
           this.setState({ hasAccess, loading: false });
         }
       },
       () => {
         if (this.mounted) {
-          this.setState({ hasAccess: false, loading: false });
+          this.setState({ loading: false });
         }
       }
     );
@@ -104,23 +98,33 @@ export default class ProjectRowActions extends React.PureComponent<Props, State>
   };
 
   render() {
-    const { hasAccess } = this.state;
+    const { hasAccess, loading } = this.state;
 
     return (
       <>
         <ActionsDropdown onOpen={this.handleDropdownOpen}>
-          {hasAccess === true && (
-            <ActionsDropdownItem to={getComponentPermissionsUrl(this.props.project.key)}>
-              {translate('edit_permissions')}
-            </ActionsDropdownItem>
-          )}
-
-          {hasAccess === false && (
-            <ActionsDropdownItem
-              className="js-restore-access"
-              onClick={this.handleRestoreAccessClick}>
-              {translate('global_permissions.restore_access')}
+          {loading ? (
+            <ActionsDropdownItem>
+              <DeferredSpinner />
             </ActionsDropdownItem>
+          ) : (
+            <>
+              {hasAccess === true && (
+                <ActionsDropdownItem
+                  className="js-edit-permissions"
+                  to={getComponentPermissionsUrl(this.props.project.key)}>
+                  {translate('edit_permissions')}
+                </ActionsDropdownItem>
+              )}
+
+              {hasAccess === false && (
+                <ActionsDropdownItem
+                  className="js-restore-access"
+                  onClick={this.handleRestoreAccessClick}>
+                  {translate('global_permissions.restore_access')}
+                </ActionsDropdownItem>
+              )}
+            </>
           )}
 
           <ActionsDropdownItem
index 93acc646f66cc7f46ce1467b194fcf99e08ff348..9ef48c4026f4dea6c59b2f59d898f72b7f6625ca 100644 (file)
@@ -21,53 +21,97 @@ import * as React from 'react';
 import { shallow } from 'enzyme';
 import { click, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
 import ProjectRowActions, { Props } from '../ProjectRowActions';
-import { Project } from '../../../api/components';
-
-jest.mock('../../../api/components', () => ({
-  getComponentShow: jest.fn(() => Promise.reject(undefined))
-}));
+import { mockLoggedInUser } from '../../../helpers/testMocks';
+import { getComponentNavigation } from '../../../api/nav';
 
 jest.mock('../../../api/nav', () => ({
-  getComponentNavigation: jest.fn(() => Promise.resolve())
+  getComponentNavigation: jest.fn().mockResolvedValue({})
 }));
 
-const project: Project = {
-  id: '',
-  key: 'project',
-  name: 'Project',
-  organization: 'org',
-  qualifier: 'TRK',
-  visibility: 'private'
-};
-
-it('restores access', async () => {
+it('renders correctly', () => {
   const wrapper = shallowRender();
   expect(wrapper).toMatchSnapshot();
+});
 
-  wrapper.find('ActionsDropdown').prop<Function>('onOpen')();
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
+describe('restore access', () => {
+  beforeAll(() => {
+    jest.resetAllMocks();
+    (getComponentNavigation as jest.Mock).mockResolvedValue({
+      configuration: {
+        showPermissions: false
+      }
+    });
+  });
 
-  click(wrapper.find('.js-restore-access'));
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
+  it('shows the restore access action', async () => {
+    const wrapper = shallowRender();
+    wrapper.instance().handleDropdownOpen();
+    await waitAndUpdate(wrapper);
+
+    expect(getComponentNavigation).toBeCalledWith({ component: 'foo' });
+    expect(wrapper.find('.js-restore-access').exists()).toBe(true);
+  });
+
+  it('shows the restore access modal', async () => {
+    const wrapper = shallowRender();
+    wrapper.instance().handleDropdownOpen();
+    await waitAndUpdate(wrapper);
+
+    click(wrapper.find('.js-restore-access'));
+    expect(wrapper.find('RestoreAccessModal')).toMatchSnapshot();
+
+    wrapper.instance().handleRestoreAccessDone();
+    await waitAndUpdate(wrapper);
+    expect(wrapper.find('.js-restore-access').exists()).toBe(false);
+    expect(wrapper.find('RestoreAccessModal').exists()).toBe(false);
+  });
 });
 
-it('applies permission template', () => {
-  const wrapper = shallowRender();
-  click(wrapper.find('.js-apply-template'));
-  expect(wrapper.find('ApplyTemplate')).toMatchSnapshot();
+describe('permissions', () => {
+  beforeAll(() => {
+    jest.resetAllMocks();
+    (getComponentNavigation as jest.Mock).mockResolvedValue({
+      configuration: {
+        showPermissions: true
+      }
+    });
+  });
+
+  it('shows the update permissions action', async () => {
+    const wrapper = shallowRender();
+    wrapper.instance().handleDropdownOpen();
+    await waitAndUpdate(wrapper);
+    expect(wrapper.find('.js-edit-permissions').exists()).toBe(true);
+  });
+
+  it('shows the apply permission template modal', async () => {
+    const wrapper = shallowRender();
+    wrapper.instance().handleDropdownOpen();
+    await waitAndUpdate(wrapper);
+
+    click(wrapper.find('.js-apply-template'));
+    expect(wrapper.find('ApplyTemplate')).toMatchSnapshot();
+
+    wrapper.instance().handleApplyTemplateClose();
+    await waitAndUpdate(wrapper);
+    expect(wrapper.find('ApplyTemplate').exists()).toBe(false);
+  });
 });
 
 function shallowRender(props: Partial<Props> = {}) {
-  const wrapper = shallow(
+  return shallow<ProjectRowActions>(
     <ProjectRowActions
-      currentUser={{ login: 'admin' }}
+      currentUser={mockLoggedInUser()}
       organization="org"
-      project={project}
+      project={{
+        id: 'foo',
+        key: 'foo',
+        name: 'Foo',
+        organization: 'bar',
+        qualifier: 'TRK',
+        visibility: 'private'
+      }}
       {...props}
     />
   );
-  (wrapper.instance() as ProjectRowActions).mounted = true;
-  return wrapper;
 }
index bbe0c710dd03032a6ee819d8378193d98c3f8fa3..7bd39b7f1f8dc8aef83127d9f86eb805f003c890 100644 (file)
@@ -1,7 +1,9 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders 1`] = `
-<tr>
+<tr
+  data-project-key="project"
+>
   <td
     className="thin"
   >
@@ -95,7 +97,9 @@ exports[`renders 1`] = `
 `;
 
 exports[`renders 2`] = `
-<tr>
+<tr
+  data-project-key="project"
+>
   <td
     className="thin"
   >
index a9f533c9cbefeddb54871374aaf5f28f2efa58b5..f69a8fde2d2094deb583df4204491f3969b3389e 100644 (file)
@@ -1,15 +1,15 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`applies permission template 1`] = `
+exports[`permissions shows the apply permission template modal 1`] = `
 <ApplyTemplate
   onClose={[Function]}
   organization="org"
   project={
     Object {
-      "id": "",
-      "key": "project",
-      "name": "Project",
-      "organization": "org",
+      "id": "foo",
+      "key": "foo",
+      "name": "Foo",
+      "organization": "bar",
       "qualifier": "TRK",
       "visibility": "private",
     }
@@ -17,7 +17,7 @@ exports[`applies permission template 1`] = `
 />
 `;
 
-exports[`restores access 1`] = `
+exports[`renders correctly 1`] = `
 <Fragment>
   <ActionsDropdown
     onOpen={[Function]}
@@ -32,63 +32,28 @@ exports[`restores access 1`] = `
 </Fragment>
 `;
 
-exports[`restores access 2`] = `
-<Fragment>
-  <ActionsDropdown
-    onOpen={[Function]}
-  >
-    <ActionsDropdownItem
-      className="js-restore-access"
-      onClick={[Function]}
-    >
-      global_permissions.restore_access
-    </ActionsDropdownItem>
-    <ActionsDropdownItem
-      className="js-apply-template"
-      onClick={[Function]}
-    >
-      projects_role.apply_template
-    </ActionsDropdownItem>
-  </ActionsDropdown>
-</Fragment>
-`;
-
-exports[`restores access 3`] = `
-<Fragment>
-  <ActionsDropdown
-    onOpen={[Function]}
-  >
-    <ActionsDropdownItem
-      className="js-restore-access"
-      onClick={[Function]}
-    >
-      global_permissions.restore_access
-    </ActionsDropdownItem>
-    <ActionsDropdownItem
-      className="js-apply-template"
-      onClick={[Function]}
-    >
-      projects_role.apply_template
-    </ActionsDropdownItem>
-  </ActionsDropdown>
-  <RestoreAccessModal
-    currentUser={
-      Object {
-        "login": "admin",
-      }
+exports[`restore access shows the restore access modal 1`] = `
+<RestoreAccessModal
+  currentUser={
+    Object {
+      "groups": Array [],
+      "isLoggedIn": true,
+      "login": "luke",
+      "name": "Skywalker",
+      "scmAccounts": Array [],
     }
-    onClose={[Function]}
-    onRestoreAccess={[Function]}
-    project={
-      Object {
-        "id": "",
-        "key": "project",
-        "name": "Project",
-        "organization": "org",
-        "qualifier": "TRK",
-        "visibility": "private",
-      }
+  }
+  onClose={[Function]}
+  onRestoreAccess={[Function]}
+  project={
+    Object {
+      "id": "foo",
+      "key": "foo",
+      "name": "Foo",
+      "organization": "bar",
+      "qualifier": "TRK",
+      "visibility": "private",
     }
-  />
-</Fragment>
+  }
+/>
 `;