diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2019-03-07 13:30:28 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-03-26 13:37:56 +0100 |
commit | 62a8287557d8ea46c6d09a9acd679f1e3c5f01cd (patch) | |
tree | 88bdcde6d3d7379f6c82d3e757b9d6465e50cef3 /server/sonar-web | |
parent | 16f267a80f2863f4ff4c2d609584d4cc0fbaae76 (diff) | |
download | sonarqube-62a8287557d8ea46c6d09a9acd679f1e3c5f01cd.tar.gz sonarqube-62a8287557d8ea46c6d09a9acd679f1e3c5f01cd.zip |
Add tests for untested components
Diffstat (limited to 'server/sonar-web')
30 files changed, 2220 insertions, 54 deletions
diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx index 3e5e40382ee..51d7dc41985 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx @@ -27,7 +27,7 @@ import A11yProvider from './a11y/A11yProvider'; import A11ySkipLinks from './a11y/A11ySkipLinks'; import Workspace from '../../components/workspace/Workspace'; -interface Props { +export interface Props { children: React.ReactNode; footer?: React.ReactNode; location: { pathname: string }; diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/GlobalContainer-test.tsx new file mode 100644 index 00000000000..ff7aba0434b --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalContainer-test.tsx @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import GlobalContainer, { Props } from '../GlobalContainer'; +import { mockLocation } from '../../../helpers/testMocks'; + +jest.mock('../embed-docs-modal/SuggestionsProvider', () => { + class SuggestionsProvider extends React.Component { + render() { + return this.props.children; + } + } + + return { default: SuggestionsProvider }; +}); + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<Props> = {}) { + class ChildComponent extends React.Component { + render() { + return null; + } + } + + return shallow( + <GlobalContainer location={mockLocation()} {...props}> + <ChildComponent /> + </GlobalContainer> + ); +} diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ProjectAdminContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ProjectAdminContainer-test.tsx new file mode 100644 index 00000000000..2935e1b2686 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/__tests__/ProjectAdminContainer-test.tsx @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { mount, shallow } from 'enzyme'; +import ProjectAdminContainer from '../ProjectAdminContainer'; +import handleRequiredAuthorization from '../../utils/handleRequiredAuthorization'; +import { mockComponent } from '../../../helpers/testMocks'; + +jest.mock('../../utils/handleRequiredAuthorization', () => { + return { default: jest.fn() }; +}); + +class ChildComponent extends React.Component { + render() { + return null; + } +} + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should redirect for authorization if needed', () => { + mountRender({ component: mockComponent({ configuration: { showSettings: false } }) }); + expect(handleRequiredAuthorization).toBeCalled(); +}); + +it('should pass props to its children', () => { + const child = shallowRender().find(ChildComponent); + // No need to check all... + expect(child.prop('component')).toBeDefined(); + expect(child.prop('onBranchesChange')).toBeDefined(); +}); + +function mountRender(props: Partial<ProjectAdminContainer['props']> = {}) { + return mount(createComponent(props)); +} + +function shallowRender(props: Partial<ProjectAdminContainer['props']> = {}) { + return shallow(createComponent(props)); +} + +function createComponent(props: Partial<ProjectAdminContainer['props']> = {}) { + return ( + <ProjectAdminContainer + branchLikes={[]} + component={mockComponent({ configuration: { showSettings: true } })} + onBranchesChange={jest.fn()} + onComponentChange={jest.fn()} + {...props}> + <ChildComponent /> + </ProjectAdminContainer> + ); +} diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap new file mode 100644 index 00000000000..484a153d105 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalContainer-test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<SuggestionsProvider> + <A11yProvider> + <Connect(withRouter(StartupModal))> + <A11ySkipLinks /> + <div + className="global-container" + > + <div + className="page-wrapper" + id="container" + > + <div + className="page-container" + > + <Workspace> + <Connect(GlobalNav) + location={ + Object { + "action": "PUSH", + "key": "key", + "pathname": "/path", + "query": Object {}, + "search": "", + "state": Object {}, + } + } + /> + <Connect(GlobalMessages) /> + <ChildComponent /> + </Workspace> + </div> + </div> + <Connect(GlobalFooter) /> + </div> + </Connect(withRouter(StartupModal))> + </A11yProvider> +</SuggestionsProvider> +`; diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/ProjectAdminContainer-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/ProjectAdminContainer-test.tsx.snap new file mode 100644 index 00000000000..7277c60ef83 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/ProjectAdminContainer-test.tsx.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <A11ySkipTarget + anchor="admin_main" + /> + <ChildComponent + branchLikes={Array []} + component={ + Object { + "breadcrumbs": Array [], + "configuration": Object { + "showSettings": true, + }, + "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 [], + } + } + onBranchesChange={[MockFunction]} + onComponentChange={[MockFunction]} + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/app/components/a11y/__tests__/A11yProvider-test.tsx b/server/sonar-web/src/main/js/app/components/a11y/__tests__/A11yProvider-test.tsx index 98f64d3db6f..22b5e650320 100644 --- a/server/sonar-web/src/main/js/app/components/a11y/__tests__/A11yProvider-test.tsx +++ b/server/sonar-web/src/main/js/app/components/a11y/__tests__/A11yProvider-test.tsx @@ -23,16 +23,17 @@ import { A11yContextShape } from '../A11yContext'; import A11yProvider from '../A11yProvider'; import { waitAndUpdate } from '../../../../helpers/testUtils'; -const link1 = { key: 'link1', label: 'Link 1' }; +const link1 = { key: 'link1', label: 'Link 1', weight: 0 }; const link2 = { key: 'link2', label: 'Link 2', weight: -10 }; -const link3 = { key: 'link3', label: 'Link 3' }; +const link3 = { key: 'link3', label: 'Link 3', weight: 0 }; it('should allow to register new skip links', () => { const wrapper = shallowRender(); const instance = wrapper.instance(); expect(wrapper.state('links')).toEqual([]); - instance.addA11ySkipLink(link1); + // Check that an absence of weight is treated as "0". + instance.addA11ySkipLink({ ...link1, weight: undefined }); expect(wrapper.state('links')).toEqual([link1]); instance.addA11ySkipLink(link2); diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx b/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx index 061b7c1b3ad..4e93eb52bac 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx +++ b/server/sonar-web/src/main/js/apps/about/components/AboutApp.tsx @@ -60,7 +60,7 @@ interface State { projectsCount: number; } -class AboutApp extends React.PureComponent<Props, State> { +export class AboutApp extends React.PureComponent<Props, State> { mounted = false; state: State = { diff --git a/server/sonar-web/src/main/js/apps/about/components/__tests__/AboutApp-test.tsx b/server/sonar-web/src/main/js/apps/about/components/__tests__/AboutApp-test.tsx new file mode 100644 index 00000000000..f18f087816e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/about/components/__tests__/AboutApp-test.tsx @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { mount } from 'enzyme'; +import { AboutApp } from '../AboutApp'; +import { addWhitePageClass, removeWhitePageClass } from '../../../../helpers/pages'; +import { searchProjects } from '../../../../api/components'; +import { getFacet } from '../../../../api/issues'; +import { mockLocation, mockAppState, mockCurrentUser } from '../../../../helpers/testMocks'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; + +jest.mock('../../../../helpers/pages', () => ({ + addWhitePageClass: jest.fn(), + removeWhitePageClass: jest.fn() +})); + +jest.mock('../../../../api/components', () => ({ + searchProjects: jest.fn().mockResolvedValue(5) +})); + +jest.mock('../../../../api/issues', () => ({ + getFacet: jest + .fn() + .mockResolvedValue([ + { facet: { count: 5, val: 'CODE_SMELL' } }, + { facet: { count: 10, val: 'BUG' } }, + { facet: { count: 0, val: 'VULNERABILITY' } } + ]) +})); + +jest.mock('../../../../app/components/GlobalContainer', () => ({ + default: class GlobalContainer extends React.Component { + static displayName = 'GlobalContainer'; + render() { + return this.props.children; + } + } +})); + +it('should render correctly', async () => { + const wrapper = mountRender(); + await waitAndUpdate(wrapper); + + expect(wrapper).toMatchSnapshot(); + expect(addWhitePageClass).toBeCalled(); + + wrapper.unmount(); + expect(removeWhitePageClass).toBeCalled(); +}); + +it('should load issues, projects, and custom text upon mounting', () => { + const fetchAboutPageSettings = jest.fn(); + mountRender({ fetchAboutPageSettings }); + expect(fetchAboutPageSettings).toBeCalled(); + expect(searchProjects).toBeCalled(); + expect(getFacet).toBeCalled(); +}); + +function mountRender(props: Partial<AboutApp['props']> = {}) { + return mount( + <AboutApp + appState={mockAppState()} + currentUser={mockCurrentUser()} + customText="Lorem ipsum" + fetchAboutPageSettings={jest.fn()} + location={mockLocation()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/about/components/__tests__/__snapshots__/AboutApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/about/components/__tests__/__snapshots__/AboutApp-test.tsx.snap new file mode 100644 index 00000000000..c4a8c0e54b4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/about/components/__tests__/__snapshots__/AboutApp-test.tsx.snap @@ -0,0 +1,1253 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<AboutApp + appState={ + Object { + "defaultOrganization": "foo", + "edition": "community", + "productionDatabase": true, + "qualifiers": Array [ + "TRK", + ], + "settings": Object {}, + "version": "1.0", + } + } + currentUser={ + Object { + "isLoggedIn": false, + } + } + customText="Lorem ipsum" + fetchAboutPageSettings={ + [MockFunction] { + "calls": Array [ + Array [], + ], + "results": Array [ + Object { + "isThrow": false, + "value": undefined, + }, + ], + } + } + location={ + Object { + "action": "PUSH", + "key": "key", + "pathname": "/path", + "query": Object {}, + "search": "", + "state": Object {}, + } + } +> + <GlobalContainer + location={ + Object { + "action": "PUSH", + "key": "key", + "pathname": "/path", + "query": Object {}, + "search": "", + "state": Object {}, + } + } + > + <div + className="page page-limited about-page" + id="about-page" + > + <A11ySkipTarget + anchor="about_main" + > + <A11ySkipTargetInner + addA11ySkipLink={[Function]} + anchor="about_main" + removeA11ySkipLink={[Function]} + > + <span + id="a11y_target__about_main" + /> + </A11ySkipTargetInner> + </A11ySkipTarget> + <div + className="about-page-entry" + > + <div + className="about-page-intro" + > + <h1 + className="big-spacer-bottom" + > + layout.sonar.slogan + </h1> + <Link + className="button button-active big-spacer-right" + onlyActiveOnIndex={false} + style={Object {}} + to="/sessions/new" + > + <a + className="button button-active big-spacer-right" + onClick={[Function]} + style={Object {}} + > + layout.login + </a> + </Link> + <a + className="button" + href="https://redirect.sonarsource.com/doc/home.html" + rel="noopener noreferrer" + target="_blank" + > + about_page.read_documentation + </a> + </div> + <div + className="about-page-instance" + > + <AboutProjects + count={0} + loading={false} + > + <div + className="about-page-projects" + > + <div> + <div> + <Link + className="about-page-projects-link" + onlyActiveOnIndex={false} + style={Object {}} + to="/projects" + > + <a + className="about-page-projects-link" + onClick={[Function]} + style={Object {}} + > + 0 + </a> + </Link> + </div> + <div> + about_page.projects_analyzed + </div> + </div> + </div> + </AboutProjects> + <EntryIssueTypes + loading={false} + > + <div + className="about-page-projects" + > + <table + className="about-page-issue-types" + > + <tbody> + <tr> + <td + className="about-page-issue-type-number" + > + <Link + className="about-page-issue-type-link" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/issues", + "query": Object { + "resolved": "false", + "s": "CREATION_DATE", + "types": "BUG", + }, + } + } + > + <a + className="about-page-issue-type-link" + onClick={[Function]} + style={Object {}} + /> + </Link> + </td> + <td> + <span + className="little-spacer-right" + > + <BugIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M11 9h1.3l.5.8.8-.5-.8-1.3H11v-.3l2-2.3V3h-1v2l-1 1.2V5c-.1-.8-.7-1.5-1.4-1.9L11 1.8l-.7-.7-1.8 1.6-1.8-1.6-.7.7 1.5 1.3C6.7 3.5 6.1 4.2 6 5v1.1L5 5V3H4v2.3l2 2.3V8H4.2l-.7 1.2.8.5.4-.7H6v.3l-2 1.9V14h1v-2.4l1-1C6 12 7.1 13 8.4 13h.8c.7 0 1.4-.3 1.8-.9.3-.4.3-.9.2-1.4l.9.9V14h1v-2.8l-2-1.9V9zm-2 2H8V6h1v5z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </BugIcon> + </span> + issue.type.BUG.plural + </td> + </tr> + <tr> + <td + className="about-page-issue-type-number" + > + <Link + className="about-page-issue-type-link" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/issues", + "query": Object { + "resolved": "false", + "s": "CREATION_DATE", + "types": "VULNERABILITY", + }, + } + } + > + <a + className="about-page-issue-type-link" + onClick={[Function]} + style={Object {}} + /> + </Link> + </td> + <td> + <span + className="little-spacer-right" + > + <VulnerabilityIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M10.8 5H6V3.9a2.28 2.28 0 0 1 2-2.5 2.22 2.22 0 0 1 1.8 1.2.48.48 0 0 0 .7.2.48.48 0 0 0 .2-.7A3 3 0 0 0 8 .4a3.34 3.34 0 0 0-3 3.5v1.2a2.16 2.16 0 0 0-2 2.1v4.4a2.22 2.22 0 0 0 2.2 2.2h5.6a2.22 2.22 0 0 0 2.2-2.2V7.2A2.22 2.22 0 0 0 10.8 5zm-2.2 5.5v1.2H7.4v-1.2a1.66 1.66 0 0 1-1.1-1.6A1.75 1.75 0 0 1 8 7.2a1.71 1.71 0 0 1 .6 3.3z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </VulnerabilityIcon> + </span> + issue.type.VULNERABILITY.plural + </td> + </tr> + <tr> + <td + className="about-page-issue-type-number" + > + <Link + className="about-page-issue-type-link" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/issues", + "query": Object { + "resolved": "false", + "s": "CREATION_DATE", + "types": "CODE_SMELL", + }, + } + } + > + <a + className="about-page-issue-type-link" + onClick={[Function]} + style={Object {}} + /> + </Link> + </td> + <td> + <span + className="little-spacer-right" + > + <CodeSmellIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M8 2C4.7 2 2 4.7 2 8s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zm-.5 5.5h.9v.9h-.9v-.9zm-3.8.2c-.1 0-.2-.1-.2-.2 0-.4.1-1.2.6-2S5.3 4.2 5.6 4c.2 0 .3 0 .3.1l1.3 2.3c0 .1 0 .2-.1.2-.1.2-.2.3-.3.5-.1.2-.2.4-.2.5 0 .1-.1.2-.2.2l-2.7-.1zM9.9 12c-.3.2-1.1.5-2 .5-.9 0-1.7-.3-2-.5-.1 0-.1-.2-.1-.3l1.3-2.3c0-.1.1-.1.2-.1.2.1.3.1.5.1s.4 0 .5-.1c.1 0 .2 0 .2.1l1.3 2.3c.2.2.2.3.1.3zm2.5-4.1L9.7 8c-.1 0-.2-.1-.2-.2 0-.2-.1-.4-.2-.5 0-.1-.2-.3-.3-.4-.1 0-.1-.1-.1-.2l1.3-2.3c.1-.1.2-.1.3-.1.3.2 1 .7 1.5 1.5s.6 1.6.6 2c0 0-.1.1-.2.1z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </CodeSmellIcon> + </span> + issue.type.CODE_SMELL.plural + </td> + </tr> + </tbody> + </table> + </div> + </EntryIssueTypes> + </div> + </div> + <div + className="about-page-section" + dangerouslySetInnerHTML={ + Object { + "__html": "Lorem ipsum", + } + } + /> + <AboutLanguages> + <div + className="boxed-group" + > + <h2> + about_page.languages + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.languages.text + </p> + <ul + className="about-page-languages" + > + <li + key="0" + > + <a + href="https://redirect.sonarsource.com/plugins/java.html" + > + Java + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/vbnet.html" + > + VB.NET + </a> + </li> + <li + key="1" + > + <a + href="https://redirect.sonarsource.com/plugins/cpp.html" + > + C/C++ + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/plsql.html" + > + PL/SQL + </a> + </li> + <li + key="2" + > + <a + href="https://redirect.sonarsource.com/plugins/csharp.html" + > + C# + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/tsql.html" + > + T-SQL + </a> + </li> + <li + key="3" + > + <a + href="https://redirect.sonarsource.com/plugins/cobol.html" + > + COBOL + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/flex.html" + > + Flex + </a> + </li> + <li + key="4" + > + <a + href="https://redirect.sonarsource.com/plugins/abap.html" + > + ABAP + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/python.html" + > + Python + </a> + </li> + <li + key="5" + > + <a + href="https://redirect.sonarsource.com/plugins/web.html" + > + HTML + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/groovy.html" + > + Groovy + </a> + </li> + <li + key="6" + > + <a + href="https://redirect.sonarsource.com/plugins/rpg.html" + > + RPG + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/php.html" + > + PHP + </a> + </li> + <li + key="7" + > + <a + href="https://redirect.sonarsource.com/plugins/javascript.html" + > + JavaScript + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/swift.html" + > + Swift + </a> + </li> + <li + key="8" + > + <a + href="https://redirect.sonarsource.com/plugins/typescript.html" + > + TypeScript + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/vb.html" + > + Visual Basic + </a> + </li> + <li + key="9" + > + <a + href="https://redirect.sonarsource.com/plugins/objectivec.html" + > + Objective C + </a> + <br /> + <a + href="https://redirect.sonarsource.com/plugins/pli.html" + > + PL/I + </a> + </li> + <li + key="10" + > + <a + href="https://redirect.sonarsource.com/plugins/xml.html" + > + XML + </a> + <br /> + </li> + </ul> + </div> + </div> + </AboutLanguages> + <AboutQualityModel> + <div + className="boxed-group about-quality-model" + > + <h2> + about_page.quality_model + </h2> + <div + className="boxed-group-inner clearfix" + > + <div + className="flex-columns" + > + <div + className="flex-column flex-column-third" + > + <div + className="pull-left little-spacer-right" + > + <BugIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M11 9h1.3l.5.8.8-.5-.8-1.3H11v-.3l2-2.3V3h-1v2l-1 1.2V5c-.1-.8-.7-1.5-1.4-1.9L11 1.8l-.7-.7-1.8 1.6-1.8-1.6-.7.7 1.5 1.3C6.7 3.5 6.1 4.2 6 5v1.1L5 5V3H4v2.3l2 2.3V8H4.2l-.7 1.2.8.5.4-.7H6v.3l-2 1.9V14h1v-2.4l1-1C6 12 7.1 13 8.4 13h.8c.7 0 1.4-.3 1.8-.9.3-.4.3-.9.2-1.4l.9.9V14h1v-2.8l-2-1.9V9zm-2 2H8V6h1v5z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </BugIcon> + </div> + <p + className="about-page-text overflow-hidden" + > + <strong> + issue.type.BUG.plural + </strong> + + about_page.quality_model.bugs + </p> + </div> + <div + className="flex-column flex-column-third" + > + <div + className="pull-left little-spacer-right" + > + <VulnerabilityIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M10.8 5H6V3.9a2.28 2.28 0 0 1 2-2.5 2.22 2.22 0 0 1 1.8 1.2.48.48 0 0 0 .7.2.48.48 0 0 0 .2-.7A3 3 0 0 0 8 .4a3.34 3.34 0 0 0-3 3.5v1.2a2.16 2.16 0 0 0-2 2.1v4.4a2.22 2.22 0 0 0 2.2 2.2h5.6a2.22 2.22 0 0 0 2.2-2.2V7.2A2.22 2.22 0 0 0 10.8 5zm-2.2 5.5v1.2H7.4v-1.2a1.66 1.66 0 0 1-1.1-1.6A1.75 1.75 0 0 1 8 7.2a1.71 1.71 0 0 1 .6 3.3z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </VulnerabilityIcon> + </div> + <p + className="about-page-text overflow-hidden" + > + <strong> + issue.type.VULNERABILITY.plural + </strong> + + about_page.quality_model.vulnerabilities + </p> + </div> + <div + className="flex-column flex-column-third" + > + <div + className="pull-left little-spacer-right" + > + <CodeSmellIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M8 2C4.7 2 2 4.7 2 8s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zm-.5 5.5h.9v.9h-.9v-.9zm-3.8.2c-.1 0-.2-.1-.2-.2 0-.4.1-1.2.6-2S5.3 4.2 5.6 4c.2 0 .3 0 .3.1l1.3 2.3c0 .1 0 .2-.1.2-.1.2-.2.3-.3.5-.1.2-.2.4-.2.5 0 .1-.1.2-.2.2l-2.7-.1zM9.9 12c-.3.2-1.1.5-2 .5-.9 0-1.7-.3-2-.5-.1 0-.1-.2-.1-.3l1.3-2.3c0-.1.1-.1.2-.1.2.1.3.1.5.1s.4 0 .5-.1c.1 0 .2 0 .2.1l1.3 2.3c.2.2.2.3.1.3zm2.5-4.1L9.7 8c-.1 0-.2-.1-.2-.2 0-.2-.1-.4-.2-.5 0-.1-.2-.3-.3-.4-.1 0-.1-.1-.1-.2l1.3-2.3c.1-.1.2-.1.3-.1.3.2 1 .7 1.5 1.5s.6 1.6.6 2c0 0-.1.1-.2.1z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </CodeSmellIcon> + </div> + <p + className="about-page-text overflow-hidden" + > + <strong> + issue.type.CODE_SMELL.plural + </strong> + + about_page.quality_model.code_smells + </p> + </div> + </div> + </div> + </div> + </AboutQualityModel> + <div + className="flex-columns" + > + <div + className="flex-column flex-column-half about-page-group-boxes" + > + <AboutCleanCode> + <div + className="boxed-group" + > + <h2> + about_page.clean_code + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.clean_code.text + </p> + <ReadMore + link="https://redirect.sonarsource.com/doc/issues.html" + > + <div + className="big-spacer-top" + > + <a + className="about-page-link-more" + href="https://redirect.sonarsource.com/doc/issues.html" + rel="noopener noreferrer" + target="_blank" + > + <span> + about_page.read_more + </span> + </a> + </div> + </ReadMore> + </div> + </div> + </AboutCleanCode> + </div> + <div + className="flex-column flex-column-half about-page-group-boxes" + > + <AboutLeakPeriod> + <div + className="boxed-group" + > + <h2> + about_page.fix_the_leak + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.fix_the_leak_on_new_code.text + </p> + <ReadMore + link="https://redirect.sonarsource.com/doc/fix-the-leak.html" + > + <div + className="big-spacer-top" + > + <a + className="about-page-link-more" + href="https://redirect.sonarsource.com/doc/fix-the-leak.html" + rel="noopener noreferrer" + target="_blank" + > + <span> + about_page.read_more + </span> + </a> + </div> + </ReadMore> + </div> + </div> + </AboutLeakPeriod> + </div> + </div> + <div + className="flex-columns" + > + <div + className="flex-column flex-column-half about-page-group-boxes" + > + <AboutQualityGates> + <div + className="boxed-group" + > + <h2> + about_page.quality_gates + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.quality_gates.text + </p> + <ReadMore + link="https://redirect.sonarsource.com/doc/quality-gates.html" + > + <div + className="big-spacer-top" + > + <a + className="about-page-link-more" + href="https://redirect.sonarsource.com/doc/quality-gates.html" + rel="noopener noreferrer" + target="_blank" + > + <span> + about_page.read_more + </span> + </a> + </div> + </ReadMore> + </div> + </div> + </AboutQualityGates> + </div> + <div + className="flex-column flex-column-half about-page-group-boxes" + > + <AboutStandards + appState={ + Object { + "defaultOrganization": "foo", + "edition": "community", + "productionDatabase": true, + "qualifiers": Array [ + "TRK", + ], + "settings": Object {}, + "version": "1.0", + } + } + > + <div + className="boxed-group" + > + <h2> + about_page.standards + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.standards.text + </p> + <div + className="spacer-top" + > + <ul + className="list-inline" + > + <li> + <Link + className="link-with-icon" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/coding_rules", + "query": Object { + "tags": "misra", + }, + } + } + > + <a + className="link-with-icon" + onClick={[Function]} + style={Object {}} + > + <TagsIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M4.303 5.36a.94.94 0 0 0-.944-.945.94.94 0 0 0-.944.944c0 .524.42.944.944.944a.94.94 0 0 0 .944-.944zm7.866 4.246a.95.95 0 0 1-.273.663l-3.62 3.627a.95.95 0 0 1-1.334 0L1.671 8.618C1.295 8.249 1 7.534 1 7.01V3.944A.95.95 0 0 1 1.944 3H5.01c.523 0 1.238.295 1.614.67l5.271 5.265a.98.98 0 0 1 .273.67zm2.831 0a.95.95 0 0 1-.273.663l-3.62 3.627a.98.98 0 0 1-.67.273c-.384 0-.575-.177-.826-.435l3.465-3.465a.95.95 0 0 0 0-1.334L7.805 3.67C7.429 3.295 6.714 3 6.19 3h1.651c.524 0 1.239.295 1.615.67l5.271 5.265a.98.98 0 0 1 .273.67z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </TagsIcon> + <span + className="little-spacer-left" + > + MISRA + </span> + </a> + </Link> + </li> + <li> + <Link + className="link-with-icon" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/coding_rules", + "query": Object { + "tags": "cert", + }, + } + } + > + <a + className="link-with-icon" + onClick={[Function]} + style={Object {}} + > + <TagsIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M4.303 5.36a.94.94 0 0 0-.944-.945.94.94 0 0 0-.944.944c0 .524.42.944.944.944a.94.94 0 0 0 .944-.944zm7.866 4.246a.95.95 0 0 1-.273.663l-3.62 3.627a.95.95 0 0 1-1.334 0L1.671 8.618C1.295 8.249 1 7.534 1 7.01V3.944A.95.95 0 0 1 1.944 3H5.01c.523 0 1.238.295 1.614.67l5.271 5.265a.98.98 0 0 1 .273.67zm2.831 0a.95.95 0 0 1-.273.663l-3.62 3.627a.98.98 0 0 1-.67.273c-.384 0-.575-.177-.826-.435l3.465-3.465a.95.95 0 0 0 0-1.334L7.805 3.67C7.429 3.295 6.714 3 6.19 3h1.651c.524 0 1.239.295 1.615.67l5.271 5.265a.98.98 0 0 1 .273.67z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </TagsIcon> + <span + className="little-spacer-left" + > + CERT + </span> + </a> + </Link> + </li> + <li> + <Link + className="link-with-icon" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/coding_rules", + "query": Object { + "tags": "cwe", + }, + } + } + > + <a + className="link-with-icon" + onClick={[Function]} + style={Object {}} + > + <TagsIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M4.303 5.36a.94.94 0 0 0-.944-.945.94.94 0 0 0-.944.944c0 .524.42.944.944.944a.94.94 0 0 0 .944-.944zm7.866 4.246a.95.95 0 0 1-.273.663l-3.62 3.627a.95.95 0 0 1-1.334 0L1.671 8.618C1.295 8.249 1 7.534 1 7.01V3.944A.95.95 0 0 1 1.944 3H5.01c.523 0 1.238.295 1.614.67l5.271 5.265a.98.98 0 0 1 .273.67zm2.831 0a.95.95 0 0 1-.273.663l-3.62 3.627a.98.98 0 0 1-.67.273c-.384 0-.575-.177-.826-.435l3.465-3.465a.95.95 0 0 0 0-1.334L7.805 3.67C7.429 3.295 6.714 3 6.19 3h1.651c.524 0 1.239.295 1.615.67l5.271 5.265a.98.98 0 0 1 .273.67z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </TagsIcon> + <span + className="little-spacer-left" + > + CWE + </span> + </a> + </Link> + </li> + <li> + <Link + className="link-with-icon" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/coding_rules", + "query": Object { + "tags": "owasp-a1,owasp-a2,owasp-a3,owasp-a4,owasp-a5,owasp-a6,owasp-a7,owasp-a8,owasp-a9,owasp-a10", + }, + } + } + > + <a + className="link-with-icon" + onClick={[Function]} + style={Object {}} + > + <TagsIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M4.303 5.36a.94.94 0 0 0-.944-.945.94.94 0 0 0-.944.944c0 .524.42.944.944.944a.94.94 0 0 0 .944-.944zm7.866 4.246a.95.95 0 0 1-.273.663l-3.62 3.627a.95.95 0 0 1-1.334 0L1.671 8.618C1.295 8.249 1 7.534 1 7.01V3.944A.95.95 0 0 1 1.944 3H5.01c.523 0 1.238.295 1.614.67l5.271 5.265a.98.98 0 0 1 .273.67zm2.831 0a.95.95 0 0 1-.273.663l-3.62 3.627a.98.98 0 0 1-.67.273c-.384 0-.575-.177-.826-.435l3.465-3.465a.95.95 0 0 0 0-1.334L7.805 3.67C7.429 3.295 6.714 3 6.19 3h1.651c.524 0 1.239.295 1.615.67l5.271 5.265a.98.98 0 0 1 .273.67z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </TagsIcon> + <span + className="little-spacer-left" + > + OWASP Top 10 + </span> + </a> + </Link> + </li> + <li> + <Link + className="link-with-icon" + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/coding_rules", + "query": Object { + "tags": "sans-top25-porous,sans-top25-risky,sans-top25-insecure", + }, + } + } + > + <a + className="link-with-icon" + onClick={[Function]} + style={Object {}} + > + <TagsIcon> + <Icon> + <svg + height={16} + style={ + Object { + "clipRule": "evenodd", + "fillRule": "evenodd", + "strokeLinejoin": "round", + "strokeMiterlimit": 1.41421, + } + } + version="1.1" + viewBox="0 0 16 16" + width={16} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink" + > + <path + d="M4.303 5.36a.94.94 0 0 0-.944-.945.94.94 0 0 0-.944.944c0 .524.42.944.944.944a.94.94 0 0 0 .944-.944zm7.866 4.246a.95.95 0 0 1-.273.663l-3.62 3.627a.95.95 0 0 1-1.334 0L1.671 8.618C1.295 8.249 1 7.534 1 7.01V3.944A.95.95 0 0 1 1.944 3H5.01c.523 0 1.238.295 1.614.67l5.271 5.265a.98.98 0 0 1 .273.67zm2.831 0a.95.95 0 0 1-.273.663l-3.62 3.627a.98.98 0 0 1-.67.273c-.384 0-.575-.177-.826-.435l3.465-3.465a.95.95 0 0 0 0-1.334L7.805 3.67C7.429 3.295 6.714 3 6.19 3h1.651c.524 0 1.239.295 1.615.67l5.271 5.265a.98.98 0 0 1 .273.67z" + style={ + Object { + "fill": "currentColor", + } + } + /> + </svg> + </Icon> + </TagsIcon> + <span + className="little-spacer-left" + > + SANS Top 25 + </span> + </a> + </Link> + </li> + </ul> + </div> + <ReadMore + link="https://redirect.sonarsource.com/doc/rules.html" + > + <div + className="big-spacer-top" + > + <a + className="about-page-link-more" + href="https://redirect.sonarsource.com/doc/rules.html" + rel="noopener noreferrer" + target="_blank" + > + <span> + about_page.read_more + </span> + </a> + </div> + </ReadMore> + </div> + </div> + </AboutStandards> + </div> + </div> + <AboutScanners> + <div + className="boxed-group" + > + <h2> + about_page.scanners + </h2> + <div + className="boxed-group-inner" + > + <p + className="about-page-text" + > + about_page.scanners.text + </p> + <div + className="about-page-analyzers" + > + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/doc/install-configure-scanner.html" + key="sonarqube" + > + <img + alt="about_page.scanners.sonarqube" + height={60} + src="/images/scanner-logos/sonarqube.svg" + /> + </a> + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html" + key="msbuild" + > + <img + alt="about_page.scanners.msbuild" + height={60} + src="/images/scanner-logos/msbuild.svg" + /> + </a> + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/doc/install-configure-scanner-maven.html" + key="maven" + > + <img + alt="about_page.scanners.maven" + height={60} + src="/images/scanner-logos/maven.svg" + /> + </a> + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/doc/gradle.html" + key="gradle" + > + <img + alt="about_page.scanners.gradle" + height={60} + src="/images/scanner-logos/gradle.svg" + /> + </a> + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/plugins/jenkins.html" + key="jenkins" + > + <img + alt="about_page.scanners.jenkins" + height={60} + src="/images/scanner-logos/jenkins.svg" + /> + </a> + <a + className="about-page-analyzer-box" + href="https://redirect.sonarsource.com/doc/install-configure-scanner-ant.html" + key="ant" + > + <img + alt="about_page.scanners.ant" + height={60} + src="/images/scanner-logos/ant.svg" + /> + </a> + </div> + </div> + </div> + </AboutScanners> + </div> + </GlobalContainer> +</AboutApp> +`; diff --git a/server/sonar-web/src/main/js/apps/account/components/Account.tsx b/server/sonar-web/src/main/js/apps/account/components/Account.tsx index 0cb548b71ed..6ca884d44ce 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Account.tsx +++ b/server/sonar-web/src/main/js/apps/account/components/Account.tsx @@ -34,7 +34,7 @@ interface Props { customOrganizations?: boolean; } -class Account extends React.PureComponent<Props> { +export class Account extends React.PureComponent<Props> { componentDidMount() { if (!this.props.currentUser.isLoggedIn) { handleRequiredAuthentication(); diff --git a/server/sonar-web/src/main/js/apps/account/components/__tests__/Account-test.tsx b/server/sonar-web/src/main/js/apps/account/components/__tests__/Account-test.tsx new file mode 100644 index 00000000000..f109395c50f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/__tests__/Account-test.tsx @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import { Account } from '../Account'; +import { mockCurrentUser } from '../../../../helpers/testMocks'; +import handleRequiredAuthentication from '../../../../app/utils/handleRequiredAuthentication'; + +jest.mock('../../../../app/utils/handleRequiredAuthentication', () => ({ + default: jest.fn() +})); + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); +}); + +it('should not render for anonymous user', () => { + const wrapper = shallowRender({ currentUser: mockCurrentUser({ isLoggedIn: false }) }); + expect(wrapper.type()).toBe(null); + expect(handleRequiredAuthentication).toBeCalled(); +}); + +function shallowRender(props: Partial<Account['props']> = {}) { + return shallow(<Account currentUser={mockCurrentUser({ isLoggedIn: true })} {...props} />); +} diff --git a/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Account-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Account-test.tsx.snap new file mode 100644 index 00000000000..23c112c210b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/components/__tests__/__snapshots__/Account-test.tsx.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + id="account-page" +> + <Suggestions + suggestions="account" + /> + <HelmetWrapper + defaultTitle="my_account.page" + defer={true} + encodeSpecialCharacters={true} + titleTemplate="%s - my_account.page" + /> + <A11ySkipTarget + anchor="account_main" + /> + <header + className="account-header" + > + <div + className="account-container clearfix" + > + <UserCard + user={ + Object { + "isLoggedIn": true, + } + } + /> + <Nav /> + </div> + </header> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap index aa8bc5e708b..e5b6c398f32 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/__tests__/__snapshots__/Sidebar-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should display two facets 1`] = ` <div> - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="measures_filters" label="component_measures.skip_to_filters" weight={10} diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx index 5ab45e611d6..0f9760727c9 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { times } from 'lodash'; import { Location } from 'history'; import { shallow, mount } from 'enzyme'; -import { Provider } from 'react-redux'; import { CreateOrganization } from '../CreateOrganization'; import { bindAlmOrganization, @@ -37,9 +36,8 @@ import { mockOrganizationWithAdminActions, mockOrganizationWithAlm, mockAlmOrganization, - mockCurrentUser, - mockLocation, - mockStore + mockLoggedInUser, + mockLocation } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; @@ -84,7 +82,7 @@ jest.mock('../../../../helpers/storage', () => ({ remove: jest.fn() })); -const user = mockCurrentUser(); +const user = mockLoggedInUser(); const fooAlmOrganization = mockAlmOrganization({ personal: true }); const fooBarAlmOrganization = mockAlmOrganization({ avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4', @@ -314,7 +312,7 @@ it('should bind org and redirect to org home when coming from org binding', asyn .mockReturnValueOnce(orgKey); // For BIND_ORGANIZATION_KEY const wrapper = mountRender({ - currentUser: mockCurrentUser({ ...user, externalProvider: 'github' }), + currentUser: mockLoggedInUser({ ...user, externalProvider: 'github' }), location: mockLocation({ query: { installation_id } }), router: mockRouter({ push }) }); @@ -328,9 +326,7 @@ it('should bind org and redirect to org home when coming from org binding', asyn }); function mountRender(props: Partial<CreateOrganization['props']> = {}) { - return mount<CreateOrganization>( - <Provider store={mockStore()}>{createComponent(props)}</Provider> - ); + return mount<CreateOrganization>(createComponent(props)); } function shallowRender(props: Partial<CreateOrganization['props']> = {}) { diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap index 7223dbfa007..aa76ac05400 100644 --- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/CreateOrganization-test.tsx.snap @@ -17,7 +17,7 @@ exports[`should render with auto personal organization bind page 2`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header @@ -98,7 +98,7 @@ exports[`should render with auto tab displayed 1`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header @@ -188,7 +188,7 @@ exports[`should render with auto tab selected and manual disabled 2`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header @@ -315,7 +315,7 @@ exports[`should render with manual tab displayed 1`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header @@ -375,7 +375,7 @@ exports[`should render with organization bind page 2`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header @@ -503,7 +503,7 @@ exports[`should switch tabs 1`] = ` <div className="page page-limited huge-spacer-top huge-spacer-bottom" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="create_org_main" /> <header diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx new file mode 100644 index 00000000000..3df094a3191 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPage-test.tsx @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import CreateProjectPage from '../CreateProjectPage'; +import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; +import { isSonarCloud } from '../../../../helpers/system'; + +jest.mock('../../../../helpers/system', () => ({ + isSonarCloud: jest.fn().mockReturnValue(false) +})); + +it('should render correctly for SonarQube', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); +}); + +it('should render correctly for SonarCloud', () => { + (isSonarCloud as jest.Mock).mockReturnValue(true); + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); +}); + +function shallowRender(props = {}) { + return shallow( + <CreateProjectPage + location={mockLocation()} + params={{}} + router={mockRouter()} + routes={[]} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap new file mode 100644 index 00000000000..f6dad80158a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/CreateProjectPage-test.tsx.snap @@ -0,0 +1,71 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for SonarCloud 1`] = ` +<Fragment> + <A11ySkipTarget + anchor="create_project_main" + /> + <Connect(withCurrentUser(whenLoggedIn(Connect(withUserOrganizations(Connect(CreateProjectPageSonarCloud)))))) + location={ + Object { + "action": "PUSH", + "key": "key", + "pathname": "/path", + "query": Object {}, + "search": "", + "state": Object {}, + } + } + params={Object {}} + router={ + Object { + "createHref": [MockFunction], + "createPath": [MockFunction], + "go": [MockFunction], + "goBack": [MockFunction], + "goForward": [MockFunction], + "isActive": [MockFunction], + "push": [MockFunction], + "replace": [MockFunction], + "setRouteLeaveHook": [MockFunction], + } + } + routes={Array []} + /> +</Fragment> +`; + +exports[`should render correctly for SonarQube 1`] = ` +<Fragment> + <A11ySkipTarget + anchor="create_project_main" + /> + <Connect(withCurrentUser(whenLoggedIn(CreateProjectPageSonarQube))) + location={ + Object { + "action": "PUSH", + "key": "key", + "pathname": "/path", + "query": Object {}, + "search": "", + "state": Object {}, + } + } + params={Object {}} + router={ + Object { + "createHref": [MockFunction], + "createPath": [MockFunction], + "go": [MockFunction], + "goBack": [MockFunction], + "goForward": [MockFunction], + "isActive": [MockFunction], + "push": [MockFunction], + "replace": [MockFunction], + "setRouteLeaveHook": [MockFunction], + } + } + routes={Array []} + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx new file mode 100644 index 00000000000..82d70d4876a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import { addSideBarClass, removeSideBarClass } from '../../../../helpers/pages'; +import App from '../App'; + +jest.mock('../../../../components/common/ScreenPositionHelper', () => ({ + default: class ScreenPositionHelper extends React.Component<{ + children: (pos: { top: number }) => React.ReactNode; + }> { + static displayName = 'ScreenPositionHelper'; + render() { + return this.props.children({ top: 0 }); + } + } +})); + +jest.mock( + 'Docs/../static/SonarQubeNavigationTree.json', + () => [ + { + title: 'SonarQube', + children: ['/lorem/ipsum/'] + } + ], + { virtual: true } +); + +jest.mock( + 'Docs/../static/SonarCloudNavigationTree.json', + () => [ + { + title: 'SonarCloud', + children: ['/lorem/ipsum/'] + } + ], + { virtual: true } +); + +jest.mock('../../../../helpers/pages', () => ({ + addSideBarClass: jest.fn(), + removeSideBarClass: jest.fn() +})); + +jest.mock('../../pages', () => { + const { mockDocumentationEntry } = require.requireActual('../../../../helpers/testMocks'); + return { + default: () => [mockDocumentationEntry()] + }; +}); + +it('should render correctly', () => { + const wrapper = shallowRender(); + + expect(wrapper).toMatchSnapshot(); + expect(addSideBarClass).toBeCalled(); + + expect(wrapper.find('ScreenPositionHelper').dive()).toMatchSnapshot(); + + wrapper.unmount(); + expect(removeSideBarClass).toBeCalled(); +}); + +it("should show a 404 if the page doesn't exist", () => { + const wrapper = shallowRender({ params: { splat: 'unknown' } }); + expect(wrapper).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<App['props']> = {}) { + return shallow(<App params={{ splat: 'lorem/ipsum' }} {...props} />); +} diff --git a/server/sonar-web/src/main/js/apps/documentation/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/__snapshots__/App-test.tsx.snap new file mode 100644 index 00000000000..5537387b421 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/__snapshots__/App-test.tsx.snap @@ -0,0 +1,118 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="layout-page" +> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="Lorem | documentation.page_title" + > + <meta + content="noindex nofollow" + name="robots" + /> + </HelmetWrapper> + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> + <div + className="layout-page-main" + > + <div + className="layout-page-main-inner" + > + <div + className="boxed-group" + > + <A11ySkipTarget + anchor="documentation_main" + /> + <DocMarkdownBlock + className="documentation-content cut-margins boxed-group-inner" + content="Lorem ipsum dolor sit amet fredum" + displayH1={true} + stickyToc={true} + /> + </div> + </div> + </div> +</div> +`; + +exports[`should render correctly 2`] = ` +<div + className="layout-page-side" + style={ + Object { + "top": 0, + } + } +> + <div + className="layout-page-side-inner" + > + <div + className="layout-page-filters" + > + <div + className="documentation-page-header" + > + <A11ySkipTarget + anchor="documentation_menu" + label="documentation.skip_to_nav" + weight={10} + /> + <Link + onlyActiveOnIndex={false} + style={Object {}} + to="/documentation/" + > + <h1> + documentation.page + </h1> + </Link> + </div> + <Sidebar + pages={ + Array [ + Object { + "content": "Lorem ipsum dolor sit amet fredum", + "navTitle": undefined, + "relativeName": "Lorem", + "text": "Lorem ipsum dolor sit amet fredum", + "title": "Lorem", + "url": "/lorem/ipsum", + }, + ] + } + splat="lorem/ipsum" + /> + </div> + </div> +</div> +`; + +exports[`should show a 404 if the page doesn't exist 1`] = ` +<Fragment> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="documentation.page_title" + > + <meta + content="noindex nofollow" + name="robots" + /> + </HelmetWrapper> + <A11ySkipTarget + anchor="documentation_main" + /> + <NotFound + withContainer={false} + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap index 7247252f779..78bbd758aa1 100644 --- a/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/organizationMembers/__tests__/__snapshots__/OrganizationMembers-test.tsx.snap @@ -12,7 +12,7 @@ exports[`should fetch members and render for non-admin 1`] = ` <Suggestions suggestions="organization_members" /> - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="members_main" /> <MembersPageHeader @@ -41,7 +41,7 @@ exports[`should fetch members and render for non-admin 2`] = ` <Suggestions suggestions="organization_members" /> - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="members_main" /> <MembersPageHeader diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap index 033ecba6e9f..324678b7dc9 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.tsx.snap @@ -13,7 +13,7 @@ exports[`should render correctly 1`] = ` encodeSpecialCharacters={true} title="project_activity.page" /> - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="activity_main" /> <ProjectActivityPageHeader diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap index 09bb32b5c55..d41f0b2801d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap @@ -21,7 +21,7 @@ exports[`renders 1`] = ` <div className="layout-page-main" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="projects_main" /> <div @@ -166,7 +166,7 @@ exports[`renders 2`] = ` <div className="layout-page-main" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="projects_main" /> <div @@ -277,7 +277,7 @@ exports[`renders correctly empty organization 2`] = ` <div className="layout-page-main" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="projects_main" /> <ContextConsumer> @@ -308,7 +308,7 @@ exports[`renders correctly empty organization 3`] = ` <div className="layout-page-main" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="projects_main" /> <div diff --git a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap index 91094ac224e..2884f4f0c57 100644 --- a/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/securityReports/components/__tests__/__snapshots__/App-test.tsx.snap @@ -16,7 +16,7 @@ exports[`handle checkbox for cwe display 1`] = ` <header className="page-header" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="security_main" /> <h1 @@ -102,7 +102,7 @@ exports[`handle checkbox for cwe display 2`] = ` <header className="page-header" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="security_main" /> <h1 @@ -252,7 +252,7 @@ exports[`renders owaspTop10 1`] = ` <header className="page-header" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="security_main" /> <h1 @@ -396,7 +396,7 @@ exports[`renders sansTop25 1`] = ` <header className="page-header" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="security_main" /> <h1 @@ -482,7 +482,7 @@ exports[`renders with cwe 1`] = ` <header className="page-header" > - <Connect(A11ySkipTarget) + <A11ySkipTarget anchor="security_main" /> <h1 diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx index 31112b25bfd..70658e1779e 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx @@ -47,7 +47,7 @@ interface State { domains: T.WebApi.Domain[]; } -class WebApiApp extends React.PureComponent<Props, State> { +export class WebApiApp extends React.PureComponent<Props, State> { mounted = false; state: State = { domains: [] }; diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/WebApiApp-test.tsx b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/WebApiApp-test.tsx new file mode 100644 index 00000000000..626e2f76bb9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/WebApiApp-test.tsx @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 { shallow } from 'enzyme'; +import { WebApiApp } from '../WebApiApp'; +import { fetchWebApi } from '../../../../api/web-api'; +import { addSideBarClass, removeSideBarClass } from '../../../../helpers/pages'; +import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; + +jest.mock('../../../../api/web-api', () => ({ + fetchWebApi: jest.fn().mockResolvedValue([ + { + actions: [], + description: 'foo', + internal: true, + path: 'foo/bar', + since: '1.0' + } + ]) +})); + +jest.mock('../../../../helpers/pages', () => ({ + addSideBarClass: jest.fn(), + removeSideBarClass: jest.fn() +})); + +jest.mock('../../../../components/common/ScreenPositionHelper', () => ({ + default: class ScreenPositionHelper extends React.Component<{ + children: (pos: { top: number }) => React.ReactNode; + }> { + static displayName = 'ScreenPositionHelper'; + render() { + return this.props.children({ top: 0 }); + } + } +})); + +it('should render correctly', async () => { + (global as any).scrollTo = jest.fn(); + + const wrapper = shallowRender(); + + expect(addSideBarClass).toBeCalled(); + expect(fetchWebApi).toBeCalled(); + + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('ScreenPositionHelper').dive()).toMatchSnapshot(); + + wrapper.unmount(); + expect(removeSideBarClass).toBeCalled(); +}); + +function shallowRender(props: Partial<WebApiApp['props']> = {}) { + return shallow( + <WebApiApp + location={mockLocation()} + params={{ splat: 'foo/bar' }} + router={mockRouter()} + routes={[]} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/WebApiApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/WebApiApp-test.tsx.snap new file mode 100644 index 00000000000..db788e7b310 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/WebApiApp-test.tsx.snap @@ -0,0 +1,119 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="layout-page" +> + <Suggestions + suggestions="api_documentation" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="api_documentation.page" + /> + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> + <div + className="layout-page-main" + > + <div + className="layout-page-main-inner" + > + <Domain + domain={ + Object { + "actions": Array [], + "deprecatedSince": undefined, + "description": "foo", + "internal": true, + "path": "foo/bar", + "since": "1.0", + } + } + key="foo/bar" + query={ + Object { + "deprecated": false, + "internal": false, + "search": "", + } + } + /> + </div> + </div> +</div> +`; + +exports[`should render correctly 2`] = ` +<div + className="layout-page-side" + style={ + Object { + "top": 0, + } + } +> + <div + className="layout-page-side-inner" + > + <div + className="layout-page-filters" + > + <A11ySkipTarget + anchor="webapi_main" + /> + <div + className="web-api-page-header" + > + <Link + onlyActiveOnIndex={false} + style={Object {}} + to="/web_api/" + > + <h1> + api_documentation.page + </h1> + </Link> + </div> + <Search + onSearch={[Function]} + onToggleDeprecated={[Function]} + onToggleInternal={[Function]} + query={ + Object { + "deprecated": false, + "internal": false, + "search": "", + } + } + /> + <Menu + domains={ + Array [ + Object { + "actions": Array [], + "deprecatedSince": undefined, + "description": "foo", + "internal": true, + "path": "foo/bar", + "since": "1.0", + }, + ] + } + query={ + Object { + "deprecated": false, + "internal": false, + "search": "", + } + } + splat="foo/bar" + /> + </div> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/whenLoggedIn-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/whenLoggedIn-test.tsx index 147340ac776..967c3d8b1df 100644 --- a/server/sonar-web/src/main/js/components/hoc/__tests__/whenLoggedIn-test.tsx +++ b/server/sonar-web/src/main/js/components/hoc/__tests__/whenLoggedIn-test.tsx @@ -19,9 +19,9 @@ */ import * as React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { createStore } from 'redux'; import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication'; import { whenLoggedIn } from '../whenLoggedIn'; +import { mockStore } from '../../../helpers/testMocks'; jest.mock('../../../app/utils/handleRequiredAuthentication', () => ({ default: jest.fn() @@ -36,14 +36,11 @@ class X extends React.Component { const UnderTest = whenLoggedIn(X); it('should render for logged in user', () => { - const store = createStore(state => state, { users: { currentUser: { isLoggedIn: true } } }); - const wrapper = shallow(<UnderTest />, { context: { store } }); - expect(getRenderedType(wrapper)).toBe(X); + expect(getRenderedType(shallowRender())).toBe(X); }); it('should not render for anonymous user', () => { - const store = createStore(state => state, { users: { currentUser: { isLoggedIn: false } } }); - const wrapper = shallow(<UnderTest />, { context: { store } }); + const wrapper = shallowRender(false); expect(getRenderedType(wrapper)).toBe(null); expect(handleRequiredAuthentication).toBeCalled(); }); @@ -54,3 +51,9 @@ function getRenderedType(wrapper: ShallowWrapper) { .dive() .type(); } + +function shallowRender(isLoggedIn = true) { + return shallow(<UnderTest />, { + context: { store: mockStore({ users: { currentUser: { isLoggedIn } } }) } + }); +} diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx index d9f819a3485..2a379a3423d 100644 --- a/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx +++ b/server/sonar-web/src/main/js/components/hoc/__tests__/withCurrentUser-test.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { shallow } from 'enzyme'; -import { createStore } from 'redux'; import { withCurrentUser } from '../withCurrentUser'; +import { mockStore } from '../../../helpers/testMocks'; class X extends React.Component<{ currentUser: T.CurrentUser }> { render() { @@ -32,8 +32,9 @@ const UnderTest = withCurrentUser(X); it('should pass logged in user', () => { const currentUser = { isLoggedIn: false }; - const store = createStore(state => state, { users: { currentUser } }); - const wrapper = shallow(<UnderTest />, { context: { store } }); + const wrapper = shallow(<UnderTest />, { + context: { store: mockStore({ users: { currentUser } }) } + }); expect(wrapper.dive().type()).toBe(X); expect(wrapper.dive().prop('currentUser')).toBe(currentUser); }); diff --git a/server/sonar-web/src/main/js/components/hoc/__tests__/withUserOrganizations-test.tsx b/server/sonar-web/src/main/js/components/hoc/__tests__/withUserOrganizations-test.tsx index 72850e444d9..72f025bad08 100644 --- a/server/sonar-web/src/main/js/components/hoc/__tests__/withUserOrganizations-test.tsx +++ b/server/sonar-web/src/main/js/components/hoc/__tests__/withUserOrganizations-test.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { shallow } from 'enzyme'; -import { createStore } from 'redux'; import { withUserOrganizations } from '../withUserOrganizations'; +import { mockStore } from '../../../helpers/testMocks'; jest.mock('../../../api/organizations', () => ({ getOrganizations: jest.fn() })); @@ -37,10 +37,14 @@ const UnderTest = withUserOrganizations(X); it('should pass user organizations and logged in user', () => { const org = { key: 'my-org', name: 'My Organization' }; - const store = createStore(state => state, { - organizations: { byKey: { 'my-org': org }, my: ['my-org'] } + const wrapper = shallow(<UnderTest />, { + context: { + store: mockStore({ + organizations: { byKey: { 'my-org': org }, my: ['my-org'] } + }) + }, + disableLifecycleMethods: true }); - const wrapper = shallow(<UnderTest />, { context: { store }, disableLifecycleMethods: true }); const wrappedComponent = wrapper.dive(); expect(wrappedComponent.type()).toBe(X); expect(wrappedComponent.prop('userOrganizations')).toEqual([org]); diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index 5a1ff87a358..a14df2435cc 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -18,10 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { InjectedRouter } from 'react-router'; -import { Store } from 'redux'; +import { Store, createStore } from 'redux'; import { Location } from 'history'; import { ParsedAnalysis } from '../apps/projectActivity/utils'; import { Profile } from '../apps/quality-profiles/types'; +import { DocumentationEntry } from '../apps/documentation/utils'; export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication { return { @@ -369,12 +370,20 @@ export function mockLongLivingBranch( }; } -export function mockStore(overrides: Partial<Store> = {}): Store { +export function mockStore(state: any = {}, reducer = (state: any) => state): Store { + return createStore(reducer, state); +} + +export function mockDocumentationEntry( + overrides: Partial<DocumentationEntry> = {} +): DocumentationEntry { return { - dispatch: jest.fn(), - getState: jest.fn(), - subscribe: jest.fn(), - replaceReducer: jest.fn(), + content: 'Lorem ipsum dolor sit amet fredum', + relativeName: 'Lorem', + navTitle: undefined, + text: 'Lorem ipsum dolor sit amet fredum', + title: 'Lorem', + url: '/lorem/ipsum', ...overrides }; } |