diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-12-07 09:57:36 +0100 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-12-11 09:41:15 +0100 |
commit | ae63a6af4780af4527dd453af7ed8923ed6bd07f (patch) | |
tree | 1cdae4d553dc4fddbb3539b8455aed01ee0f545c /server/sonar-web/src/main | |
parent | da80cca3a9fc255e0ebaa1f7ea2ba098725ee7c2 (diff) | |
download | sonarqube-ae63a6af4780af4527dd453af7ed8923ed6bd07f.tar.gz sonarqube-ae63a6af4780af4527dd453af7ed8923ed6bd07f.zip |
SONAR-10067 add "Restore Access" action on projects management page
Diffstat (limited to 'server/sonar-web/src/main')
13 files changed, 547 insertions, 133 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx index f92519a6a9e..c714647d45a 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/App.tsx @@ -31,6 +31,7 @@ import { Organization } from '../../app/types'; import { translate } from '../../helpers/l10n'; export interface Props { + currentUser: { login: string }; hasProvisionPermission?: boolean; onVisibilityChange: (visibility: string) => void; organization: Organization; @@ -191,6 +192,7 @@ export default class App extends React.PureComponent<Props, State> { /> <Projects + currentUser={this.props.currentUser} ready={this.state.ready} projects={this.state.projects} selection={this.state.selection} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx index 0b00f3eba76..199132d1a0b 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx @@ -22,7 +22,7 @@ import { connect } from 'react-redux'; import App from './App'; import { Organization } from '../../app/types'; import { onFail } from '../../store/rootActions'; -import { getAppState, getOrganizationByKey } from '../../store/rootReducer'; +import { getAppState, getOrganizationByKey, getCurrentUser } from '../../store/rootReducer'; import { receiveOrganizations } from '../../store/organizations/duck'; import { changeProjectVisibility } from '../../api/organizations'; import { fetchOrganization } from '../../apps/organizations/actions'; @@ -32,6 +32,7 @@ interface Props { defaultOrganization: string; qualifiers: string[]; }; + currentUser: { login: string }; fetchOrganization: (organization: string) => void; onVisibilityChange: (organization: Organization, visibility: string) => void; onRequestFail: (error: any) => void; @@ -64,6 +65,7 @@ class AppContainer extends React.PureComponent<Props> { return ( <App + currentUser={this.props.currentUser} hasProvisionPermission={organization.canProvisionProjects} onVisibilityChange={this.handleVisibilityChange} organization={organization} @@ -75,6 +77,7 @@ class AppContainer extends React.PureComponent<Props> { const mapStateToProps = (state: any, ownProps: Props) => ({ appState: getAppState(state), + currentUser: getCurrentUser(state), organization: ownProps.organization || getOrganizationByKey(state, getAppState(state).defaultOrganization) }); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx index ce7df2a1c43..9b243f7ec6b 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx @@ -19,17 +19,17 @@ */ import * as React from 'react'; import { Link } from 'react-router'; +import ProjectRowActions from './ProjectRowActions'; import { Project } from './utils'; import { Visibility } from '../../app/types'; import PrivateBadge from '../../components/common/PrivateBadge'; import Checkbox from '../../components/controls/Checkbox'; import QualifierIcon from '../../components/shared/QualifierIcon'; -import { translate } from '../../helpers/l10n'; -import { getComponentPermissionsUrl } from '../../helpers/urls'; import DateTooltipFormatter from '../../components/intl/DateTooltipFormatter'; interface Props { - onApplyTemplateClick: (project: Project) => void; + currentUser: { login: string }; + onApplyTemplate: (project: Project) => void; onProjectCheck: (project: Project, checked: boolean) => void; project: Project; selected: boolean; @@ -40,12 +40,6 @@ export default class ProjectRow extends React.PureComponent<Props> { this.props.onProjectCheck(this.props.project, checked); }; - handleApplyTemplateClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { - event.preventDefault(); - event.currentTarget.blur(); - this.props.onApplyTemplateClick(this.props.project); - }; - render() { const { project, selected } = this.props; @@ -82,23 +76,11 @@ export default class ProjectRow extends React.PureComponent<Props> { </td> <td className="thin nowrap"> - <div className="dropdown"> - <button className="dropdown-toggle" data-toggle="dropdown"> - {translate('actions')} <i className="icon-dropdown" /> - </button> - <ul className="dropdown-menu dropdown-menu-right"> - <li> - <Link to={getComponentPermissionsUrl(project.key)}> - {translate('edit_permissions')} - </Link> - </li> - <li> - <a className="js-apply-template" href="#" onClick={this.handleApplyTemplateClick}> - {translate('projects_role.apply_template')} - </a> - </li> - </ul> - </div> + <ProjectRowActions + currentUser={this.props.currentUser} + onApplyTemplate={this.props.onApplyTemplate} + project={project} + /> </td> </tr> ); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx new file mode 100644 index 00000000000..f05ba7eef69 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -0,0 +1,153 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import { Link } from 'react-router'; +import RestoreAccessModal from './RestoreAccessModal'; +import { Project } from './utils'; +import { getComponentShow } from '../../api/components'; +import { getComponentNavigation } from '../../api/nav'; +import { translate } from '../../helpers/l10n'; +import { getComponentPermissionsUrl } from '../../helpers/urls'; + +export interface Props { + currentUser: { login: string }; + onApplyTemplate: (project: Project) => void; + project: Project; +} + +interface State { + hasAccess?: boolean; + loading: boolean; + restoreAccessModal: boolean; +} + +export default class ProjectRowActions extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = { loading: false, restoreAccessModal: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + 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(this.props.project.key), + getComponentShow(this.props.project.key) + ]).then( + ([navResponse]) => { + if (this.mounted) { + const hasAccess = Boolean( + navResponse.configuration && navResponse.configuration.showPermissions + ); + this.setState({ hasAccess, loading: false }); + } + }, + () => { + if (this.mounted) { + this.setState({ hasAccess: false, loading: false }); + } + } + ); + }; + + handleDropdownClick = () => { + if (this.state.hasAccess === undefined && !this.state.loading) { + this.fetchPermissions(); + } + }; + + handleApplyTemplateClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.onApplyTemplate(this.props.project); + }; + + handleRestoreAccessClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { + event.preventDefault(); + event.currentTarget.blur(); + this.setState({ restoreAccessModal: true }); + }; + + handleRestoreAccessClose = () => this.setState({ restoreAccessModal: false }); + + handleRestoreAccessDone = () => { + this.setState({ hasAccess: true, restoreAccessModal: false }); + }; + + render() { + const { hasAccess, loading } = this.state; + + return ( + <div className="dropdown"> + <button + className="dropdown-toggle" + data-toggle="dropdown" + onClick={this.handleDropdownClick}> + {translate('actions')} <i className="icon-dropdown" /> + </button> + {loading ? ( + <div className="dropdown-menu dropdown-menu-right"> + <i className="spinner spacer-left" /> + </div> + ) : ( + <ul className="dropdown-menu dropdown-menu-right"> + {hasAccess === true && ( + <li> + <Link to={getComponentPermissionsUrl(this.props.project.key)}> + {translate('edit_permissions')} + </Link> + </li> + )} + + {hasAccess === false && ( + <li> + <a className="js-restore-access" href="#" onClick={this.handleRestoreAccessClick}> + {translate('global_permissions.restore_access')} + </a> + </li> + )} + + <li> + <a className="js-apply-template" href="#" onClick={this.handleApplyTemplateClick}> + {translate('projects_role.apply_template')} + </a> + </li> + </ul> + )} + + {this.state.restoreAccessModal && ( + <RestoreAccessModal + currentUser={this.props.currentUser} + onClose={this.handleRestoreAccessClose} + onRestoreAccess={this.handleRestoreAccessDone} + project={this.props.project} + /> + )} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx index 2a2b3c1cb2c..d3399685066 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx @@ -23,8 +23,10 @@ import ProjectRow from './ProjectRow'; import { Project } from './utils'; import ApplyTemplateView from '../permissions/project/views/ApplyTemplateView'; import { Organization } from '../../app/types'; +import { translate } from '../../helpers/l10n'; interface Props { + currentUser: { login: string }; onProjectDeselected: (project: string) => void; onProjectSelected: (project: string) => void; organization: Organization; @@ -42,7 +44,7 @@ export default class Projects extends React.PureComponent<Props> { } }; - onApplyTemplateClick = (project: Project) => { + handleApplyTemplate = (project: Project) => { new ApplyTemplateView({ project, organization: this.props.organization }).render(); }; @@ -54,18 +56,19 @@ export default class Projects extends React.PureComponent<Props> { <thead> <tr> <th /> - <th>Name</th> + <th>{translate('name')}</th> <th /> - <th>Key</th> - <th className="thin nowrap text-right">Last Analysis</th> + <th>{translate('key')}</th> + <th className="thin nowrap text-right">{translate('last_analysis')}</th> <th /> </tr> </thead> <tbody> {this.props.projects.map(project => ( <ProjectRow + currentUser={this.props.currentUser} key={project.key} - onApplyTemplateClick={this.onApplyTemplateClick} + onApplyTemplate={this.handleApplyTemplate} onProjectCheck={this.onProjectCheck} project={project} selected={this.props.selection.includes(project.key)} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx new file mode 100644 index 00000000000..104c71864be --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import Modal from 'react-modal'; +import { Project } from './utils'; +import { grantPermissionToUser } from '../../api/permissions'; +import { translate } from '../../helpers/l10n'; +import { FormattedMessage } from 'react-intl'; + +interface Props { + currentUser: { login: string }; + onClose: () => void; + onRestoreAccess: () => void; + project: Project; +} + +interface State { + loading: boolean; +} + +export default class BulkApplyTemplateModal extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = { loading: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { + event.preventDefault(); + this.props.onClose(); + }; + + handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { + event.preventDefault(); + this.setState({ loading: true }); + Promise.all([this.grantPermission('user'), this.grantPermission('admin')]).then( + this.props.onRestoreAccess, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + }; + + grantPermission = (permission: string) => + grantPermissionToUser( + this.props.project.key, + this.props.currentUser.login, + permission, + this.props.project.organization + ); + + render() { + const header = translate('global_permissions.restore_access'); + + return ( + <Modal + isOpen={true} + contentLabel={header} + className="modal" + overlayClassName="modal-overlay" + onRequestClose={this.props.onClose}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + + <form onSubmit={this.handleFormSubmit}> + <div className="modal-body"> + <FormattedMessage + defaultMessage={translate('global_permissions.restore_access.message')} + id="global_permissions.restore_access.message" + values={{ + browse: <strong>{translate('projects_role.user')}</strong>, + administer: <strong>{translate('projects_role.admin')}</strong> + }} + /> + </div> + + <footer className="modal-foot"> + {this.state.loading && <i className="spinner spacer-right" />} + <button disabled={this.state.loading}>{translate('restore')}</button> + <a className="js-modal-close" href="#" onClick={this.handleCancelClick}> + {translate('cancel')} + </a> + </footer> + </form> + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx index 0fb6b1d21d0..36dde628a47 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx @@ -138,6 +138,7 @@ it('changes default project visibility', () => { function mountRender(props?: { [P in keyof Props]?: Props[P] }) { return mount( <App + currentUser={{ login: 'foo' }} hasProvisionPermission={true} onVisibilityChange={jest.fn()} organization={organization} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx index b1d052dbb86..20e180df5cd 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import ProjectRow from '../ProjectRow'; import { Visibility } from '../../../app/types'; -import { click } from '../../../helpers/testUtils'; const project = { key: 'project', @@ -44,17 +43,11 @@ it('checks project', () => { expect(onProjectCheck).toBeCalledWith(project, false); }); -it('applies permission template', () => { - const onApplyTemplateClick = jest.fn(); - const wrapper = shallowRender({ onApplyTemplateClick }); - click(wrapper.find('.js-apply-template')); - expect(onApplyTemplateClick).toBeCalledWith(project); -}); - function shallowRender(props?: any) { return shallow( <ProjectRow - onApplyTemplateClick={jest.fn()} + currentUser={{ login: 'foo' }} + onApplyTemplate={jest.fn()} onProjectCheck={jest.fn()} project={project} selected={true} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx new file mode 100644 index 00000000000..b6678d3e45e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact 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 * as React from 'react'; +import { shallow } from 'enzyme'; +import ProjectRowActions, { Props } from '../ProjectRowActions'; +import { Visibility } from '../../../app/types'; +import { click } from '../../../helpers/testUtils'; + +jest.mock('../../../api/components', () => ({ + getComponentShow: jest.fn(() => Promise.reject(undefined)) +})); + +jest.mock('../../../api/nav', () => ({ + getComponentNavigation: jest.fn(() => Promise.resolve()) +})); + +const project = { + id: '', + key: 'project', + name: 'Project', + organization: 'org', + qualifier: 'TRK', + visibility: Visibility.Private +}; + +it('restores access', async () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); + + click(wrapper.find('.dropdown-toggle')); + await new Promise(setImmediate); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + + click(wrapper.find('.js-restore-access')); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); +}); + +it('applies permission template', () => { + const onApplyTemplate = jest.fn(); + const wrapper = shallowRender({ onApplyTemplate }); + click(wrapper.find('.js-apply-template')); + expect(onApplyTemplate).toBeCalledWith(project); +}); + +function shallowRender(props: Partial<Props> = {}) { + const wrapper = shallow( + <ProjectRowActions + currentUser={{ login: 'admin' }} + onApplyTemplate={jest.fn()} + project={project} + {...props} + /> + ); + (wrapper.instance() as ProjectRowActions).mounted = true; + return wrapper; +} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx index 792e29ccc48..23f93bc8d26 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx @@ -59,13 +59,14 @@ it('opens modal to apply permission template', () => { wrapper .find('ProjectRow') .first() - .prop<Function>('onApplyTemplateClick')(projects[0]); + .prop<Function>('onApplyTemplate')(projects[0]); expect(ApplyTemplateView).toBeCalledWith({ organization, project: projects[0] }); }); function shallowRender(props?: any) { return shallow( <Projects + currentUser={{ login: 'foo' }} onProjectDeselected={jest.fn()} onProjectSelected={jest.fn()} organization={organization} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap index 87d61db023e..49178bc7abd 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap @@ -64,49 +64,22 @@ exports[`renders 1`] = ` <td className="thin nowrap" > - <div - className="dropdown" - > - <button - className="dropdown-toggle" - data-toggle="dropdown" - > - actions - - <i - className="icon-dropdown" - /> - </button> - <ul - className="dropdown-menu dropdown-menu-right" - > - <li> - <Link - onlyActiveOnIndex={false} - style={Object {}} - to={ - Object { - "pathname": "/project_roles", - "query": Object { - "id": "project", - }, - } - } - > - edit_permissions - </Link> - </li> - <li> - <a - className="js-apply-template" - href="#" - onClick={[Function]} - > - projects_role.apply_template - </a> - </li> - </ul> - </div> + <ProjectRowActions + currentUser={ + Object { + "login": "foo", + } + } + onApplyTemplate={[Function]} + project={ + Object { + "key": "project", + "name": "Project", + "qualifier": "TRK", + "visibility": "private", + } + } + /> </td> </tr> `; @@ -173,49 +146,23 @@ exports[`renders 2`] = ` <td className="thin nowrap" > - <div - className="dropdown" - > - <button - className="dropdown-toggle" - data-toggle="dropdown" - > - actions - - <i - className="icon-dropdown" - /> - </button> - <ul - className="dropdown-menu dropdown-menu-right" - > - <li> - <Link - onlyActiveOnIndex={false} - style={Object {}} - to={ - Object { - "pathname": "/project_roles", - "query": Object { - "id": "project", - }, - } - } - > - edit_permissions - </Link> - </li> - <li> - <a - className="js-apply-template" - href="#" - onClick={[Function]} - > - projects_role.apply_template - </a> - </li> - </ul> - </div> + <ProjectRowActions + currentUser={ + Object { + "login": "foo", + } + } + onApplyTemplate={[Function]} + project={ + Object { + "key": "project", + "lastAnalysisDate": "2017-04-08T00:00:00.000Z", + "name": "Project", + "qualifier": "TRK", + "visibility": "private", + } + } + /> </td> </tr> `; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap new file mode 100644 index 00000000000..b376ea3f6a6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap @@ -0,0 +1,131 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`restores access 1`] = ` +<div + className="dropdown" +> + <button + className="dropdown-toggle" + data-toggle="dropdown" + onClick={[Function]} + > + actions + + <i + className="icon-dropdown" + /> + </button> + <ul + className="dropdown-menu dropdown-menu-right" + > + <li> + <a + className="js-apply-template" + href="#" + onClick={[Function]} + > + projects_role.apply_template + </a> + </li> + </ul> +</div> +`; + +exports[`restores access 2`] = ` +<div + className="dropdown" +> + <button + className="dropdown-toggle" + data-toggle="dropdown" + onClick={[Function]} + > + actions + + <i + className="icon-dropdown" + /> + </button> + <ul + className="dropdown-menu dropdown-menu-right" + > + <li> + <a + className="js-restore-access" + href="#" + onClick={[Function]} + > + global_permissions.restore_access + </a> + </li> + <li> + <a + className="js-apply-template" + href="#" + onClick={[Function]} + > + projects_role.apply_template + </a> + </li> + </ul> +</div> +`; + +exports[`restores access 3`] = ` +<div + className="dropdown" +> + <button + className="dropdown-toggle" + data-toggle="dropdown" + onClick={[Function]} + > + actions + + <i + className="icon-dropdown" + /> + </button> + <ul + className="dropdown-menu dropdown-menu-right" + > + <li> + <a + className="js-restore-access" + href="#" + onClick={[Function]} + > + global_permissions.restore_access + </a> + </li> + <li> + <a + className="js-apply-template" + href="#" + onClick={[Function]} + > + projects_role.apply_template + </a> + </li> + </ul> + <BulkApplyTemplateModal + currentUser={ + Object { + "login": "admin", + } + } + onClose={[Function]} + onRestoreAccess={[Function]} + project={ + Object { + "id": "", + "key": "project", + "name": "Project", + "organization": "org", + "qualifier": "TRK", + "visibility": "private", + } + } + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap index 2c60880eb28..7bae7a95d3e 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap @@ -9,23 +9,28 @@ exports[`renders list of projects 1`] = ` <tr> <th /> <th> - Name + name </th> <th /> <th> - Key + key </th> <th className="thin nowrap text-right" > - Last Analysis + last_analysis </th> <th /> </tr> </thead> <tbody> <ProjectRow - onApplyTemplateClick={[Function]} + currentUser={ + Object { + "login": "foo", + } + } + onApplyTemplate={[Function]} onProjectCheck={[Function]} project={ Object { @@ -38,7 +43,12 @@ exports[`renders list of projects 1`] = ` selected={true} /> <ProjectRow - onApplyTemplateClick={[Function]} + currentUser={ + Object { + "login": "foo", + } + } + onApplyTemplate={[Function]} onProjectCheck={[Function]} project={ Object { |