@@ -20,7 +20,11 @@ | |||
import throwGlobalError from '../app/utils/throwGlobalError'; | |||
import { getJSON, post, postJSON } from '../helpers/request'; | |||
import { BranchParameters } from '../types/branch-like'; | |||
import { QualityGateApplicationStatus, QualityGateProjectStatus } from '../types/quality-gates'; | |||
import { | |||
QualityGateApplicationStatus, | |||
QualityGateProjectStatus, | |||
SearchPermissionsParameters | |||
} from '../types/quality-gates'; | |||
export function fetchQualityGates(): Promise<{ | |||
actions: { create: boolean }; | |||
@@ -124,3 +128,7 @@ export function getQualityGateProjectStatus( | |||
.then(r => r.projectStatus) | |||
.catch(throwGlobalError); | |||
} | |||
export function searchUsers(data: SearchPermissionsParameters): Promise<{ users: T.UserBase[] }> { | |||
return getJSON('/api/qualitygates/search_users', data).catch(throwGlobalError); | |||
} |
@@ -19,12 +19,17 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import HelpTooltip from '../../../components/controls/HelpTooltip'; | |||
import { withCurrentUser } from '../../../components/hoc/withCurrentUser'; | |||
import { Alert } from '../../../components/ui/Alert'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { hasGlobalPermission } from '../../../helpers/users'; | |||
import { Permissions } from '../../../types/permissions'; | |||
import Conditions from './Conditions'; | |||
import Projects from './Projects'; | |||
import QualityGatePermissions from './QualityGatePermissions'; | |||
export interface DetailsContentProps { | |||
currentUser: T.CurrentUser; | |||
isDefault?: boolean; | |||
metrics: T.Dict<T.Metric>; | |||
onAddCondition: (condition: T.Condition) => void; | |||
@@ -35,9 +40,11 @@ export interface DetailsContentProps { | |||
} | |||
export function DetailsContent(props: DetailsContentProps) { | |||
const { isDefault, metrics, qualityGate, updatedConditionId } = props; | |||
const { currentUser, isDefault, metrics, qualityGate, updatedConditionId } = props; | |||
const conditions = qualityGate.conditions || []; | |||
const actions = qualityGate.actions || ({} as any); | |||
const actions = qualityGate.actions || {}; | |||
const displayPermissions = hasGlobalPermission(currentUser, Permissions.QualityGateAdmin); | |||
return ( | |||
<div className="layout-page-main-inner"> | |||
@@ -48,7 +55,7 @@ export function DetailsContent(props: DetailsContentProps) { | |||
)} | |||
<Conditions | |||
canEdit={actions.manageConditions} | |||
canEdit={Boolean(actions.manageConditions)} | |||
conditions={conditions} | |||
metrics={metrics} | |||
onAddCondition={props.onAddCondition} | |||
@@ -58,31 +65,38 @@ export function DetailsContent(props: DetailsContentProps) { | |||
updatedConditionId={updatedConditionId} | |||
/> | |||
<div className="quality-gate-section" id="quality-gate-projects"> | |||
<header className="display-flex-center spacer-bottom"> | |||
<h3>{translate('quality_gates.projects')}</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div className="big-padded-top big-padded-bottom"> | |||
{translate('quality_gates.projects.help')} | |||
</div> | |||
} | |||
/> | |||
</header> | |||
{isDefault ? ( | |||
translate('quality_gates.projects_for_default') | |||
) : ( | |||
<Projects | |||
canEdit={actions.associateProjects} | |||
// pass unique key to re-mount the component when the quality gate changes | |||
key={qualityGate.id} | |||
qualityGate={qualityGate} | |||
/> | |||
<div className="display-flex-row huge-spacer-top"> | |||
<div className="quality-gate-section width-50 big-padded-right" id="quality-gate-projects"> | |||
<header className="display-flex-center spacer-bottom"> | |||
<h3>{translate('quality_gates.projects')}</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div className="big-padded-top big-padded-bottom"> | |||
{translate('quality_gates.projects.help')} | |||
</div> | |||
} | |||
/> | |||
</header> | |||
{isDefault ? ( | |||
translate('quality_gates.projects_for_default') | |||
) : ( | |||
<Projects | |||
canEdit={actions.associateProjects} | |||
// pass unique key to re-mount the component when the quality gate changes | |||
key={qualityGate.id} | |||
qualityGate={qualityGate} | |||
/> | |||
)} | |||
</div> | |||
{displayPermissions && ( | |||
<div className="width-50 big-padded-left"> | |||
<QualityGatePermissions qualityGate={qualityGate} /> | |||
</div> | |||
)} | |||
</div> | |||
</div> | |||
); | |||
} | |||
export default React.memo(DetailsContent); | |||
export default React.memo(withCurrentUser(DetailsContent)); |
@@ -0,0 +1,40 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 * as React from 'react'; | |||
import Avatar from '../../../components/ui/Avatar'; | |||
interface Props { | |||
user: T.UserBase; | |||
} | |||
export default function PermissionItem(props: Props) { | |||
const { user } = props; | |||
return ( | |||
<div className="display-flex-row"> | |||
<Avatar className="spacer-right" hash={user.avatar} name={user.name} size={32} /> | |||
<div className="overflow-hidden"> | |||
<strong>{user.name}</strong> | |||
<div className="note">{user.login}</div> | |||
</div> | |||
</div> | |||
); | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 * as React from 'react'; | |||
import { searchUsers } from '../../../api/quality-gates'; | |||
import QualityGatePermissionsRenderer from './QualityGatePermissionsRenderer'; | |||
interface Props { | |||
qualityGate: T.QualityGate; | |||
} | |||
interface State { | |||
loading: boolean; | |||
users: T.UserBase[]; | |||
} | |||
export default class QualityGatePermissions extends React.Component<Props, State> { | |||
mounted = false; | |||
state: State = { | |||
loading: true, | |||
users: [] | |||
}; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.fetchPermissions(); | |||
} | |||
componentDidUpdate(newProps: Props) { | |||
if (this.props.qualityGate.id !== newProps.qualityGate.id) { | |||
this.fetchPermissions(); | |||
} | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
fetchPermissions = async () => { | |||
const { qualityGate } = this.props; | |||
this.setState({ loading: true }); | |||
const { users } = await searchUsers({ | |||
qualityGate: qualityGate.id, | |||
selected: 'selected' | |||
}).catch(() => ({ | |||
users: [] | |||
})); | |||
if (this.mounted) { | |||
this.setState({ loading: false, users }); | |||
} | |||
}; | |||
render() { | |||
const { loading, users } = this.state; | |||
return <QualityGatePermissionsRenderer loading={loading} users={users} />; | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 * as React from 'react'; | |||
import DeferredSpinner from '../../../components/ui/DeferredSpinner'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import PermissionItem from './PermissionItem'; | |||
export interface QualityGatePermissionsRendererProps { | |||
loading: boolean; | |||
users: T.UserBase[]; | |||
} | |||
export default function QualityGatePermissionsRenderer(props: QualityGatePermissionsRendererProps) { | |||
const { loading, users } = props; | |||
return ( | |||
<div> | |||
<header className="display-flex-center spacer-bottom"> | |||
<h3>{translate('quality_gates.permissions')}</h3> | |||
</header> | |||
<p className="spacer-bottom">{translate('quality_gates.permissions.help')}</p> | |||
<DeferredSpinner loading={loading}> | |||
<ul> | |||
{users.map(user => ( | |||
<li key={user.login} className="spacer-top"> | |||
<PermissionItem user={user} /> | |||
</li> | |||
))} | |||
</ul> | |||
</DeferredSpinner> | |||
</div> | |||
); | |||
} |
@@ -20,7 +20,7 @@ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityGate } from '../../../../helpers/mocks/quality-gates'; | |||
import { mockCondition } from '../../../../helpers/testMocks'; | |||
import { mockCondition, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import { DetailsContent, DetailsContentProps } from '../DetailsContent'; | |||
it('should render correctly', () => { | |||
@@ -29,11 +29,15 @@ it('should render correctly', () => { | |||
expect( | |||
shallowRender({ isDefault: true, qualityGate: mockQualityGate({ conditions: [] }) }) | |||
).toMatchSnapshot('is default, no conditions'); | |||
expect( | |||
shallowRender({ currentUser: mockLoggedInUser({ permissions: { global: ['gateadmin'] } }) }) | |||
).toMatchSnapshot('Admin'); | |||
}); | |||
function shallowRender(props: Partial<DetailsContentProps> = {}) { | |||
return shallow( | |||
<DetailsContent | |||
currentUser={mockLoggedInUser()} | |||
metrics={{}} | |||
onAddCondition={jest.fn()} | |||
onRemoveCondition={jest.fn()} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { mockUser } from '../../../../helpers/testMocks'; | |||
import PermissionItem from '../PermissionItem'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
function shallowRender() { | |||
return shallow(<PermissionItem user={mockUser()} />); | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { searchUsers } from '../../../../api/quality-gates'; | |||
import { mockQualityGate } from '../../../../helpers/mocks/quality-gates'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import QualityGatePermissions from '../QualityGatePermissions'; | |||
jest.mock('../../../../api/quality-gates', () => ({ | |||
searchUsers: jest.fn().mockResolvedValue({ users: [] }) | |||
})); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should fetch users', async () => { | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(searchUsers).toBeCalledWith({ qualityGate: '1', selected: 'selected' }); | |||
}); | |||
function shallowRender(overrides: Partial<QualityGatePermissions['props']> = {}) { | |||
return shallow(<QualityGatePermissions qualityGate={mockQualityGate()} {...overrides} />); | |||
} |
@@ -0,0 +1,36 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2021 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 { mockUser } from '../../../../helpers/testMocks'; | |||
import QualityGatePermissionsRenderer, { | |||
QualityGatePermissionsRendererProps | |||
} from '../QualityGatePermissionsRenderer'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('with users'); | |||
expect(shallowRender({ users: [] })).toMatchSnapshot('with no users'); | |||
}); | |||
function shallowRender(overrides: Partial<QualityGatePermissionsRendererProps> = {}) { | |||
return shallow( | |||
<QualityGatePermissionsRenderer loading={false} users={[mockUser()]} {...overrides} /> | |||
); | |||
} |
@@ -24,7 +24,7 @@ exports[`should render correctly: loaded 1`] = ` | |||
refreshItem={[Function]} | |||
refreshList={[MockFunction]} | |||
/> | |||
<Memo(DetailsContent) | |||
<Memo(Connect(withCurrentUser(DetailsContent))) | |||
isDefault={false} | |||
metrics={Object {}} | |||
onAddCondition={[Function]} |
@@ -1,10 +1,11 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: is default 1`] = ` | |||
exports[`should render correctly: Admin 1`] = ` | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<Connect(withAppState(Conditions)) | |||
canEdit={false} | |||
conditions={ | |||
Array [ | |||
Object { | |||
@@ -35,27 +36,132 @@ exports[`should render correctly: is default 1`] = ` | |||
} | |||
/> | |||
<div | |||
className="quality-gate-section" | |||
id="quality-gate-projects" | |||
className="display-flex-row huge-spacer-top" | |||
> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
<div | |||
className="quality-gate-section width-50 big-padded-right" | |||
id="quality-gate-projects" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
} | |||
/> | |||
</header> | |||
<Projects | |||
key="1" | |||
qualityGate={ | |||
Object { | |||
"conditions": Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
], | |||
"id": "1", | |||
"name": "qualitygate", | |||
} | |||
} | |||
/> | |||
</header> | |||
quality_gates.projects_for_default | |||
</div> | |||
<div | |||
className="width-50 big-padded-left" | |||
> | |||
<QualityGatePermissions | |||
qualityGate={ | |||
Object { | |||
"conditions": Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
], | |||
"id": "1", | |||
"name": "qualitygate", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: is default 1`] = ` | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<Connect(withAppState(Conditions)) | |||
canEdit={false} | |||
conditions={ | |||
Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
] | |||
} | |||
metrics={Object {}} | |||
onAddCondition={[MockFunction]} | |||
onRemoveCondition={[MockFunction]} | |||
onSaveCondition={[MockFunction]} | |||
qualityGate={ | |||
Object { | |||
"conditions": Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
], | |||
"id": "1", | |||
"name": "qualitygate", | |||
} | |||
} | |||
/> | |||
<div | |||
className="display-flex-row huge-spacer-top" | |||
> | |||
<div | |||
className="quality-gate-section width-50 big-padded-right" | |||
id="quality-gate-projects" | |||
> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
} | |||
/> | |||
</header> | |||
quality_gates.projects_for_default | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
@@ -71,6 +177,7 @@ exports[`should render correctly: is default, no conditions 1`] = ` | |||
quality_gates.is_default_no_conditions | |||
</Alert> | |||
<Connect(withAppState(Conditions)) | |||
canEdit={false} | |||
conditions={Array []} | |||
metrics={Object {}} | |||
onAddCondition={[MockFunction]} | |||
@@ -85,27 +192,31 @@ exports[`should render correctly: is default, no conditions 1`] = ` | |||
} | |||
/> | |||
<div | |||
className="quality-gate-section" | |||
id="quality-gate-projects" | |||
className="display-flex-row huge-spacer-top" | |||
> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
<div | |||
className="quality-gate-section width-50 big-padded-right" | |||
id="quality-gate-projects" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
} | |||
/> | |||
</header> | |||
quality_gates.projects_for_default | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
} | |||
/> | |||
</header> | |||
quality_gates.projects_for_default | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
@@ -115,6 +226,7 @@ exports[`should render correctly: is not default 1`] = ` | |||
className="layout-page-main-inner" | |||
> | |||
<Connect(withAppState(Conditions)) | |||
canEdit={false} | |||
conditions={ | |||
Array [ | |||
Object { | |||
@@ -145,43 +257,47 @@ exports[`should render correctly: is not default 1`] = ` | |||
} | |||
/> | |||
<div | |||
className="quality-gate-section" | |||
id="quality-gate-projects" | |||
className="display-flex-row huge-spacer-top" | |||
> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
<div | |||
className="quality-gate-section width-50 big-padded-right" | |||
id="quality-gate-projects" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.projects | |||
</h3> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay={ | |||
<div | |||
className="big-padded-top big-padded-bottom" | |||
> | |||
quality_gates.projects.help | |||
</div> | |||
} | |||
/> | |||
</header> | |||
<Projects | |||
key="1" | |||
qualityGate={ | |||
Object { | |||
"conditions": Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
], | |||
"id": "1", | |||
"name": "qualitygate", | |||
} | |||
} | |||
/> | |||
</header> | |||
<Projects | |||
key="1" | |||
qualityGate={ | |||
Object { | |||
"conditions": Array [ | |||
Object { | |||
"error": "10", | |||
"id": 1, | |||
"metric": "coverage", | |||
"op": "LT", | |||
}, | |||
], | |||
"id": "1", | |||
"name": "qualitygate", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; |
@@ -0,0 +1,25 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="display-flex-row" | |||
> | |||
<Connect(Avatar) | |||
className="spacer-right" | |||
name="John Doe" | |||
size={32} | |||
/> | |||
<div | |||
className="overflow-hidden" | |||
> | |||
<strong> | |||
John Doe | |||
</strong> | |||
<div | |||
className="note" | |||
> | |||
john.doe | |||
</div> | |||
</div> | |||
</div> | |||
`; |
@@ -0,0 +1,8 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<QualityGatePermissionsRenderer | |||
loading={true} | |||
users={Array []} | |||
/> | |||
`; |
@@ -0,0 +1,61 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: with no users 1`] = ` | |||
<div> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.permissions | |||
</h3> | |||
</header> | |||
<p | |||
className="spacer-bottom" | |||
> | |||
quality_gates.permissions.help | |||
</p> | |||
<DeferredSpinner | |||
loading={false} | |||
> | |||
<ul /> | |||
</DeferredSpinner> | |||
</div> | |||
`; | |||
exports[`should render correctly: with users 1`] = ` | |||
<div> | |||
<header | |||
className="display-flex-center spacer-bottom" | |||
> | |||
<h3> | |||
quality_gates.permissions | |||
</h3> | |||
</header> | |||
<p | |||
className="spacer-bottom" | |||
> | |||
quality_gates.permissions.help | |||
</p> | |||
<DeferredSpinner | |||
loading={false} | |||
> | |||
<ul> | |||
<li | |||
className="spacer-top" | |||
key="john.doe" | |||
> | |||
<PermissionItem | |||
user={ | |||
Object { | |||
"active": true, | |||
"local": true, | |||
"login": "john.doe", | |||
"name": "John Doe", | |||
} | |||
} | |||
/> | |||
</li> | |||
</ul> | |||
</DeferredSpinner> | |||
</div> | |||
`; |
@@ -21,5 +21,6 @@ | |||
export enum Permissions { | |||
Admin = 'admin', | |||
ProjectCreation = 'provisioning', | |||
ApplicationCreation = 'applicationcreator' | |||
ApplicationCreation = 'applicationcreator', | |||
QualityGateAdmin = 'gateadmin' | |||
} |
@@ -80,3 +80,9 @@ export interface QualityGateStatusCondition { | |||
export interface QualityGateStatusConditionEnhanced extends QualityGateStatusCondition { | |||
measure: T.MeasureEnhanced; | |||
} | |||
export interface SearchPermissionsParameters { | |||
qualityGate: string; | |||
q?: string; | |||
selected?: 'all' | 'selected' | 'deselected'; | |||
} |
@@ -1734,6 +1734,9 @@ quality_gates.built_in.description.1=This quality gate is provided by default. | |||
quality_gates.built_in.description.2=It will automatically be updated with the latest recommendations. | |||
quality_gates.status=Quality Gate status | |||
quality_gates.help=A Quality Gate is a set of measure-based, Boolean conditions. It helps you know immediately whether your projects are production-ready. Ideally, all projects will use the same quality gate. Each project's Quality Gate status is displayed prominently on its homepage. | |||
quality_gates.permissions=Permissions | |||
quality_gates.permissions.help=Users with the global "Manage Quality Gates" permission can manage this Quality Gate. | |||
#------------------------------------------------------------------------------ | |||
# |