return getJSON('/api/project_tags/search', data).catch(throwGlobalError);
}
+export function setApplicationTags(data: { application: string; tags: string }): Promise<void> {
+ return post('/api/applications/set_tags', data);
+}
+
export function setProjectTags(data: { project: string; tags: string }): Promise<void> {
return post('/api/project_tags/set', data);
}
</div>
<div className="overflow-y-auto">
- {(component.description || !isApp) && (
- <div className="big-padded bordered-bottom">
- <div className="display-flex-center">
- <h3 className="spacer-right">{translate('project.info.description')}</h3>
- {component.visibility && (
- <PrivacyBadgeContainer
- organization={undefined}
- qualifier={component.qualifier}
- tooltipProps={{ projectKey: component.key }}
- visibility={component.visibility}
- />
- )}
- </div>
-
- {component.description && <p className="spacer-bottom">{component.description}</p>}
-
- {!isApp && (
- <MetaTags component={component} onComponentChange={props.onComponentChange} />
+ <div className="big-padded bordered-bottom">
+ <div className="display-flex-center">
+ <h3 className="spacer-right">{translate('project.info.description')}</h3>
+ {component.visibility && (
+ <PrivacyBadgeContainer
+ organization={undefined}
+ qualifier={component.qualifier}
+ tooltipProps={{ projectKey: component.key }}
+ visibility={component.visibility}
+ />
)}
</div>
- )}
+
+ {component.description && <p>{component.description}</p>}
+
+ <MetaTags component={component} onComponentChange={props.onComponentChange} />
+ </div>
<div className="big-padded bordered-bottom it__project-loc-value">
<MetaSize component={component} measures={measures} />
expect(shallowRender({ component })).toMatchSnapshot('default');
});
+it('should render without description', () => {
+ const component = mockComponent({ description: undefined });
+ expect(shallowRender({ component })).toMatchSnapshot();
+});
+
it('should handle missing quality profiles and quality gates', () => {
expect(
shallowRender({
<div
className="overflow-y-auto"
>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <div
+ className="display-flex-center"
+ >
+ <h3
+ className="spacer-right"
+ >
+ project.info.description
+ </h3>
+ </div>
+ <MetaTags
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "APP",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ onComponentChange={[MockFunction]}
+ />
+ </div>
<div
className="big-padded bordered-bottom it__project-loc-value"
>
</div>
</Fragment>
`;
+
+exports[`should render without description 1`] = `
+<Fragment>
+ <div>
+ <h2
+ className="big-padded bordered-bottom"
+ >
+ project.info.title
+ </h2>
+ </div>
+ <div
+ className="overflow-y-auto"
+ >
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <div
+ className="display-flex-center"
+ >
+ <h3
+ className="spacer-right"
+ >
+ project.info.description
+ </h3>
+ </div>
+ <MetaTags
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "description": undefined,
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ onComponentChange={[MockFunction]}
+ />
+ </div>
+ <div
+ className="big-padded bordered-bottom it__project-loc-value"
+ >
+ <MetaSize
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "description": undefined,
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ measures={Array []}
+ />
+ </div>
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <MetaQualityGate
+ qualityGate={
+ Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ }
+ }
+ />
+ <Connect(MetaQualityProfiles)
+ headerClassName="big-spacer-top"
+ profiles={
+ Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ]
+ }
+ />
+ </div>
+ <MetaLinks
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "description": undefined,
+ "key": "my-project",
+ "name": "MyProject",
+ "organization": "foo",
+ "qualifier": "TRK",
+ "qualityGate": Object {
+ "isDefault": true,
+ "key": "30",
+ "name": "Sonar way",
+ },
+ "qualityProfiles": Array [
+ Object {
+ "deleted": false,
+ "key": "my-qp",
+ "language": "ts",
+ "name": "Sonar way",
+ },
+ ],
+ "tags": Array [],
+ }
+ }
+ />
+ <div
+ className="big-padded bordered-bottom"
+ >
+ <MetaKey
+ componentKey="my-project"
+ qualifier="TRK"
+ />
+ </div>
+ <Memo(DrawerLink)
+ label="overview.badges.get_badge.TRK"
+ onPageChange={[MockFunction]}
+ to={1}
+ />
+ <Memo(DrawerLink)
+ label="project.info.to_notifications"
+ onPageChange={[MockFunction]}
+ to={2}
+ />
+ </div>
+</Fragment>
+`;
import Dropdown from 'sonar-ui-common/components/controls/Dropdown';
import { PopupPlacement } from 'sonar-ui-common/components/ui/popups';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { setProjectTags } from '../../../../../../api/components';
+import { setApplicationTags, setProjectTags } from '../../../../../../api/components';
import TagsList from '../../../../../../components/tags/TagsList';
+import { ComponentQualifier } from '../../../../../../types/component';
import MetaTagsSelector from './MetaTagsSelector';
interface Props {
right: containerPos.width - eltPos.width
});
+ setTags = (values: string[]) => {
+ const { component } = this.props;
+
+ if (component.qualifier === ComponentQualifier.Application) {
+ return setApplicationTags({
+ application: component.key,
+ tags: values.join(',')
+ });
+ } else {
+ return setProjectTags({
+ project: component.key,
+ tags: values.join(',')
+ });
+ }
+ };
+
handleSetProjectTags = (values: string[]) => {
- setProjectTags({
- project: this.props.component.key,
- tags: values.join(',')
- }).then(
+ this.setTags(values).then(
() => this.props.onComponentChange({ tags: values }),
() => {}
);
if (this.canUpdateTags()) {
return (
- <div className="project-info-tags" ref={card => (this.card = card)}>
+ <div className="big-spacer-top project-info-tags" ref={card => (this.card = card)}>
<Dropdown
closeOnClick={false}
closeOnClickOutside={true}
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { setApplicationTags, setProjectTags } from '../../../../../../../api/components';
import { mockComponent } from '../../../../../../../helpers/testMocks';
+import { ComponentQualifier } from '../../../../../../../types/component';
import MetaTags from '../MetaTags';
-const component = mockComponent({
- configuration: {
- showSettings: false
- }
-});
+jest.mock('../../../../../../../api/components', () => ({
+ setApplicationTags: jest.fn().mockResolvedValue(true),
+ setProjectTags: jest.fn().mockResolvedValue(true)
+}));
-const componentWithTags = mockComponent({
- key: 'my-second-project',
- tags: ['foo', 'bar'],
- configuration: {
- showSettings: true
- },
- name: 'MySecondProject'
+beforeEach(() => {
+ jest.clearAllMocks();
});
it('should render without tags and admin rights', () => {
- expect(
- shallow(<MetaTags component={component} onComponentChange={jest.fn()} />, {
- disableLifecycleMethods: true
- })
- ).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('should render with tags and admin rights', () => {
- expect(
- shallow(<MetaTags component={componentWithTags} onComponentChange={jest.fn()} />, {
- disableLifecycleMethods: true
- })
- ).toMatchSnapshot();
+ const component = mockComponent({
+ key: 'my-second-project',
+ tags: ['foo', 'bar'],
+ configuration: {
+ showSettings: true
+ },
+ name: 'MySecondProject'
+ });
+
+ expect(shallowRender({ component })).toMatchSnapshot();
+});
+
+it('should set tags for a project', () => {
+ const wrapper = shallowRender();
+
+ wrapper.instance().handleSetProjectTags(['tag1', 'tag2']);
+
+ expect(setProjectTags).toHaveBeenCalled();
+ expect(setApplicationTags).not.toHaveBeenCalled();
});
+
+it('should set tags for an app', () => {
+ const wrapper = shallowRender({
+ component: mockComponent({ qualifier: ComponentQualifier.Application })
+ });
+
+ wrapper.instance().handleSetProjectTags(['tag1', 'tag2']);
+
+ expect(setProjectTags).not.toHaveBeenCalled();
+ expect(setApplicationTags).toHaveBeenCalled();
+});
+
+function shallowRender(overrides: Partial<MetaTags['props']> = {}) {
+ const component = mockComponent({
+ configuration: {
+ showSettings: false
+ }
+ });
+
+ return shallow<MetaTags>(
+ <MetaTags component={component} onComponentChange={jest.fn()} {...overrides} />
+ );
+}
exports[`should render with tags and admin rights 1`] = `
<div
- className="project-info-tags"
+ className="big-spacer-top project-info-tags"
>
<Dropdown
closeOnClick={false}