diff options
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 #------------------------------------------------------------------------------ # |