aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLike.tsx5
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/__tests__/__snapshots__/CurrentBranchLike-test.tsx.snap5
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx46
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TutorialsApp-test.tsx.snap41
-rw-r--r--server/sonar-web/src/main/js/apps/tutorials/routes.ts29
-rw-r--r--server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx27
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DocumentationTooltip-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/manual/BuildToolForm.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/manual/__tests__/BuildToolForm-test.tsx5
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
14 files changed, 204 insertions, 17 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLike.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLike.tsx
index b17252d148a..63a819444bc 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLike.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/CurrentBranchLike.tsx
@@ -106,6 +106,11 @@ export function CurrentBranchLike(props: CurrentBranchLikeProps) {
{
href: '/documentation/analysis/pull-request/',
label: translate('branch_like_navigation.only_one_branch.pr_analysis')
+ },
+ {
+ href: `/tutorials?id=${component.key}`,
+ label: translate('branch_like_navigation.tutorial_for_ci'),
+ inPlace: true
}
]}
title={translate('branch_like_navigation.only_one_branch.title')}>
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/__tests__/__snapshots__/CurrentBranchLike-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/__tests__/__snapshots__/CurrentBranchLike-test.tsx.snap
index dcad04e6221..db23d06a772 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/__tests__/__snapshots__/CurrentBranchLike-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/__tests__/__snapshots__/CurrentBranchLike-test.tsx.snap
@@ -199,6 +199,11 @@ exports[`CurrentBranchLikeRenderer should render correctly for project when ther
"href": "/documentation/analysis/pull-request/",
"label": "branch_like_navigation.only_one_branch.pr_analysis",
},
+ Object {
+ "href": "/tutorials?id=my-project",
+ "inPlace": true,
+ "label": "branch_like_navigation.tutorial_for_ci",
+ },
]
}
title="branch_like_navigation.only_one_branch.title"
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index 76a558be16d..6455491dddd 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -59,6 +59,7 @@ import qualityProfilesRoutes from '../../apps/quality-profiles/routes';
import sessionsRoutes from '../../apps/sessions/routes';
import settingsRoutes from '../../apps/settings/routes';
import systemRoutes from '../../apps/system/routes';
+import tutorialsRoutes from '../../apps/tutorials/routes';
import usersRoutes from '../../apps/users/routes';
import webAPIRoutes from '../../apps/web-api/routes';
import webhooksRoutes from '../../apps/webhooks/routes';
@@ -189,6 +190,7 @@ function renderComponentRoutes() {
path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes}
/>
+ <RouteWithChildRoutes path="tutorials" childRoutes={tutorialsRoutes} />
<Route component={lazyLoadComponent(() => import('../components/ProjectAdminContainer'))}>
<RouteWithChildRoutes path="custom_measures" childRoutes={customMeasuresRoutes} />
<Route
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx
new file mode 100644
index 00000000000..7720191b84a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/TutorialsApp.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 handleRequiredAuthentication from 'sonar-ui-common/helpers/handleRequiredAuthentication';
+import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import TutorialSelection from '../../../components/tutorials/TutorialSelection';
+import { isLoggedIn } from '../../../helpers/users';
+
+export interface TutorialsAppProps {
+ component: T.Component;
+ currentUser: T.CurrentUser;
+}
+
+export function TutorialsApp(props: TutorialsAppProps) {
+ const { component, currentUser } = props;
+
+ if (!isLoggedIn(currentUser)) {
+ handleRequiredAuthentication();
+ return null;
+ }
+
+ return (
+ <div className="page page-limited">
+ <TutorialSelection component={component} currentUser={currentUser} />
+ </div>
+ );
+}
+
+export default withCurrentUser(TutorialsApp);
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx
new file mode 100644
index 00000000000..443e0efc00f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TutorialsApp-test.tsx
@@ -0,0 +1,41 @@
+/*
+ * 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 handleRequiredAuthentication from 'sonar-ui-common/helpers/handleRequiredAuthentication';
+import { mockComponent, mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { TutorialsApp, TutorialsAppProps } from '../TutorialsApp';
+
+jest.mock('sonar-ui-common/helpers/handleRequiredAuthentication', () => ({ default: jest.fn() }));
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should redirect if user is not logged in', () => {
+ shallowRender({ currentUser: mockCurrentUser() });
+ expect(handleRequiredAuthentication).toHaveBeenCalled();
+});
+
+function shallowRender(overrides: Partial<TutorialsAppProps> = {}) {
+ return shallow(
+ <TutorialsApp component={mockComponent()} currentUser={mockLoggedInUser()} {...overrides} />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TutorialsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TutorialsApp-test.tsx.snap
new file mode 100644
index 00000000000..7a8438fd0ef
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TutorialsApp-test.tsx.snap
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="page page-limited"
+>
+ <withRouter(TutorialSelection)
+ component={
+ Object {
+ "breadcrumbs": Array [],
+ "key": "my-project",
+ "name": "MyProject",
+ "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 [],
+ }
+ }
+ currentUser={
+ Object {
+ "groups": Array [],
+ "isLoggedIn": true,
+ "login": "luke",
+ "name": "Skywalker",
+ "scmAccounts": Array [],
+ }
+ }
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/routes.ts b/server/sonar-web/src/main/js/apps/tutorials/routes.ts
new file mode 100644
index 00000000000..3c043682d37
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/tutorials/routes.ts
@@ -0,0 +1,29 @@
+/*
+ * 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 { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
+
+const routes = [
+ {
+ indexRoute: { component: lazyLoadComponent(() => import('./components/TutorialsApp')) }
+ }
+];
+
+export default routes;
diff --git a/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx b/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
index 3eed54c305a..2e1546dd61c 100644
--- a/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
+++ b/server/sonar-web/src/main/js/components/common/DocumentationTooltip.tsx
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { Link } from 'react-router';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import DetachIcon from 'sonar-ui-common/components/icons/DetachIcon';
import { isWebUri } from 'valid-url';
@@ -26,7 +27,7 @@ export interface DocumentationTooltipProps {
children?: React.ReactNode;
className?: string;
content?: React.ReactNode;
- links?: Array<{ href: string; label: string }>;
+ links?: Array<{ href: string; label: string; inPlace?: boolean }>;
title?: string;
}
@@ -50,16 +51,22 @@ export default function DocumentationTooltip(props: DocumentationTooltipProps) {
<>
<hr className="big-spacer-top big-spacer-bottom" />
- {links.map(({ href, label }) => (
+ {links.map(({ href, label, inPlace }) => (
<div className="little-spacer-bottom" key={label}>
- <a
- className="display-inline-flex-center link-with-icon"
- href={href}
- rel="noopener noreferrer"
- target="_blank">
- {isWebUri(href) && <DetachIcon size={14} className="spacer-right" />}
- <span>{label}</span>
- </a>
+ {inPlace ? (
+ <Link to={href}>
+ <span>{label}</span>
+ </Link>
+ ) : (
+ <a
+ className="display-inline-flex-center link-with-icon"
+ href={href}
+ rel="noopener noreferrer"
+ target="_blank">
+ {isWebUri(href) && <DetachIcon size={14} className="spacer-right" />}
+ <span>{label}</span>
+ </a>
+ )}
</div>
))}
</>
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
index 35135b03e16..2297731b494 100644
--- a/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
+++ b/server/sonar-web/src/main/js/components/common/__tests__/DocumentationTooltip-test.tsx
@@ -27,7 +27,8 @@ it('renders correctly', () => {
shallowRender({
links: [
{ href: 'http://link.tosome.place', label: 'external link' },
- { href: '/documentation/guide', label: 'internal link' }
+ { href: '/documentation/guide', label: 'internal link' },
+ { href: '/projects', label: 'in place', inPlace: true }
]
})
).toMatchSnapshot('with links');
diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DocumentationTooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DocumentationTooltip-test.tsx.snap
index 423376fa2ce..522d6cfee3b 100644
--- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DocumentationTooltip-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/DocumentationTooltip-test.tsx.snap
@@ -105,6 +105,19 @@ exports[`renders correctly: with links 1`] = `
</span>
</a>
</div>
+ <div
+ className="little-spacer-bottom"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/projects"
+ >
+ <span>
+ in place
+ </span>
+ </Link>
+ </div>
</React.Fragment>
</div>
}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/BuildToolForm.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/BuildToolForm.tsx
index 5a6d2772197..ae6acfbdd81 100644
--- a/server/sonar-web/src/main/js/components/tutorials/manual/BuildToolForm.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/manual/BuildToolForm.tsx
@@ -24,7 +24,6 @@ import RenderOptions from '../components/RenderOptions';
import { BuildTools, ManualTutorialConfig, OSs } from '../types';
interface Props {
- component: T.Component;
config?: ManualTutorialConfig;
onDone: (config: ManualTutorialConfig) => void;
}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx
index f41b759f611..e82ae41c58d 100644
--- a/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx
@@ -51,7 +51,7 @@ export default class ProjectAnalysisStep extends React.PureComponent<Props, Stat
return (
<div className="boxed-group-inner">
<div className="display-flex-column">
- <BuildToolForm component={this.props.component} onDone={this.handleBuildToolSelect} />
+ <BuildToolForm onDone={this.handleBuildToolSelect} />
{this.state.config && (
<div className="huge-spacer-top">
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/BuildToolForm-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/BuildToolForm-test.tsx
index 7350d33680f..5329c3632dd 100644
--- a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/BuildToolForm-test.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/BuildToolForm-test.tsx
@@ -19,7 +19,6 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockComponent } from '../../../../helpers/testMocks';
import { BuildTools, OSs } from '../../types';
import BuildToolForm from '../BuildToolForm';
@@ -47,7 +46,5 @@ it('correctly calls the onDone prop', () => {
});
function shallowRender(props: Partial<BuildToolForm['props']> = {}) {
- return shallow<BuildToolForm>(
- <BuildToolForm component={mockComponent()} onDone={jest.fn()} {...props} />
- );
+ return shallow<BuildToolForm>(<BuildToolForm onDone={jest.fn()} {...props} />);
}
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index dd89a7b0e6e..42a36e41c0f 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -3594,6 +3594,7 @@ branch_like_navigation.only_one_branch.title=Learn how to analyze branches in So
branch_like_navigation.only_one_branch.content=Quickly setup branch analysis and get separate insights for each of your branches and Pull Requests.
branch_like_navigation.only_one_branch.documentation=Branches documentation
branch_like_navigation.only_one_branch.pr_analysis=Pull Request analysis
+branch_like_navigation.tutorial_for_ci=Show me how to set up my CI
#------------------------------------------------------------------------------
#