aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-12-18 11:53:09 +0100
committerSonarTech <sonartech@sonarsource.com>2018-12-20 11:43:28 +0100
commit3fc77718cf0e14555367dd8f3492205db3f5ce8c (patch)
tree5dc1c8bb7d11ad33f17054d64abfb37e7a28736d /server/sonar-web
parent90ed118a35ce03cd51794271ff4865e4e9c46584 (diff)
downloadsonarqube-3fc77718cf0e14555367dd8f3492205db3f5ce8c.tar.gz
sonarqube-3fc77718cf0e14555367dd8f3492205db3f5ce8c.zip
SONARCLOUD-264 Use same empty organization space in projects page
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css (renamed from server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.css)4
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx (renamed from server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx)8
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationEmpty-test.tsx (renamed from server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx)8
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationJustCreated-test.tsx.snap)2
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/routes.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx115
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap200
13 files changed, 335 insertions, 90 deletions
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
index 11f6fe3fbc5..7a805ec17ba 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
@@ -48,7 +48,7 @@ interface Props {
handleOrgDetailsFinish: (organization: T.Organization) => Promise<void>;
handleOrgDetailsStepOpen: () => void;
onDone: () => void;
- onOrgCreated: (organization: string, justCreated?: boolean) => void;
+ onOrgCreated: (organization: string) => void;
onUpgradeFail: () => void;
organization?: T.Organization;
step: Step;
@@ -72,7 +72,7 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
return bindAlmOrganization({
organization,
installationId: this.props.almInstallId
- }).then(() => this.props.onOrgCreated(organization, false));
+ }).then(() => this.props.onOrgCreated(organization));
};
handleCreateOrganization = () => {
diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
index 7ccc09db294..e368675861c 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
@@ -205,7 +205,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
this.updateUrlQuery({ almInstallId: undefined, almKey: undefined });
};
- handleOrgCreated = (organization: string, justCreated = true) => {
+ handleOrgCreated = (organization: string) => {
this.props.skipOnboarding();
if (this.isStoredTimestampValid(ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP)) {
this.props.router.push({
@@ -213,10 +213,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
state: { organization, tab: this.state.almOrganization ? 'auto' : 'manual' }
});
} else {
- this.props.router.push({
- pathname: getOrganizationUrl(organization),
- state: { justCreated }
- });
+ this.props.router.push({ pathname: getOrganizationUrl(organization) });
}
};
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
index c7999e6fa19..4026d982fab 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/AutoOrganizationCreate-test.tsx
@@ -87,7 +87,7 @@ it('should bind existing organization', async () => {
organization: 'foo'
});
await waitAndUpdate(wrapper);
- expect(onOrgCreated).toHaveBeenCalledWith('foo', false);
+ expect(onOrgCreated).toHaveBeenCalledWith('foo');
});
function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
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 4da19584a2e..ddd5ebcd85f 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
@@ -226,16 +226,7 @@ it('should redirect to organization page after creation', async () => {
wrapper.setState({ organization: boundOrganization });
wrapper.instance().handleOrgCreated('foo');
- expect(push).toHaveBeenCalledWith({
- pathname: '/organizations/foo',
- state: { justCreated: true }
- });
-
- wrapper.instance().handleOrgCreated('foo', false);
- expect(push).toHaveBeenCalledWith({
- pathname: '/organizations/foo',
- state: { justCreated: false }
- });
+ expect(push).toHaveBeenCalledWith({ pathname: '/organizations/foo' });
});
it('should redirect to projects creation page after creation', async () => {
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.css b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css
index 2779ccbce5c..55f794f1fc5 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.css
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.css
@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.organization-just-created {
- margin: 120px auto 0;
+.organization-empty {
+ margin: 100px auto 0;
width: 800px;
}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
index 89f1d217de5..d47f5675f1c 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationJustCreated.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
@@ -25,7 +25,7 @@ import { translate } from '../../../helpers/l10n';
import { OnboardingContextShape } from '../../../app/components/OnboardingContext';
import { withRouter, Router } from '../../../components/hoc/withRouter';
import '../../tutorials/styles.css';
-import './OrganizationJustCreated.css';
+import './OrganizationEmpty.css';
interface Props {
openProjectOnboarding: OnboardingContextShape;
@@ -33,7 +33,7 @@ interface Props {
router: Pick<Router, 'push'>;
}
-export class OrganizationJustCreated extends React.PureComponent<Props> {
+export class OrganizationEmpty extends React.PureComponent<Props> {
handleNewProjectClick = () => {
this.props.openProjectOnboarding(this.props.organization);
};
@@ -45,7 +45,7 @@ export class OrganizationJustCreated extends React.PureComponent<Props> {
render() {
return (
- <div className="organization-just-created">
+ <div className="organization-empty">
<h3 className="text-center">{translate('onboarding.create_organization.ready')}</h3>
<div className="onboarding-choices">
<Button className="onboarding-choice" onClick={this.handleNewProjectClick}>
@@ -66,4 +66,4 @@ export class OrganizationJustCreated extends React.PureComponent<Props> {
}
}
-export default withRouter(OrganizationJustCreated);
+export default withRouter(OrganizationEmpty);
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
index d8770331308..e74b3a8b651 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
@@ -21,7 +21,6 @@ import * as React from 'react';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import { Location } from 'history';
-import OrganizationJustCreated from './OrganizationJustCreated';
import OrganizationNavigation from '../navigation/OrganizationNavigation';
import { fetchOrganization } from '../actions';
import NotFound from '../../../app/components/NotFound';
@@ -32,7 +31,6 @@ import {
getMyOrganizations,
Store
} from '../../../store/rootReducer';
-import { OnboardingContext } from '../../../app/components/OnboardingContext';
interface OwnProps {
children?: React.ReactNode;
@@ -86,23 +84,6 @@ export class OrganizationPage extends React.PureComponent<Props, State> {
this.props.fetchOrganization(organizationKey).then(this.stopLoading, this.stopLoading);
};
- renderChildren(organization: T.Organization) {
- const { location } = this.props;
- const justCreated = Boolean(location.state && location.state.justCreated);
- return justCreated ? (
- <OnboardingContext.Consumer>
- {openProjectOnboarding => (
- <OrganizationJustCreated
- openProjectOnboarding={openProjectOnboarding}
- organization={organization}
- />
- )}
- </OnboardingContext.Consumer>
- ) : (
- this.props.children
- );
- }
-
render() {
const { organization } = this.props;
@@ -124,7 +105,7 @@ export class OrganizationPage extends React.PureComponent<Props, State> {
organization={organization}
userOrganizations={this.props.userOrganizations}
/>
- {this.renderChildren(organization)}
+ {this.props.children}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationEmpty-test.tsx
index 0bcc3631ed5..257fa9e9e27 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationJustCreated-test.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationEmpty-test.tsx
@@ -19,7 +19,7 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import { OrganizationJustCreated } from '../OrganizationJustCreated';
+import { OrganizationEmpty } from '../OrganizationEmpty';
import { click } from '../../../../helpers/testUtils';
const organization: T.Organization = { key: 'foo', name: 'Foo' };
@@ -27,7 +27,7 @@ const organization: T.Organization = { key: 'foo', name: 'Foo' };
it('should render', () => {
expect(
shallow(
- <OrganizationJustCreated
+ <OrganizationEmpty
openProjectOnboarding={jest.fn()}
organization={organization}
router={{ push: jest.fn() }}
@@ -39,7 +39,7 @@ it('should render', () => {
it('should create new project', () => {
const openProjectOnboarding = jest.fn();
const wrapper = shallow(
- <OrganizationJustCreated
+ <OrganizationEmpty
openProjectOnboarding={openProjectOnboarding}
organization={organization}
router={{ push: jest.fn() }}
@@ -52,7 +52,7 @@ it('should create new project', () => {
it('should add members', () => {
const router = { push: jest.fn() };
const wrapper = shallow(
- <OrganizationJustCreated
+ <OrganizationEmpty
openProjectOnboarding={jest.fn()}
organization={organization}
router={router}
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationJustCreated-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap
index 0ce7ab7b2fa..32086688e74 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationJustCreated-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationEmpty-test.tsx.snap
@@ -2,7 +2,7 @@
exports[`should render 1`] = `
<div
- className="organization-just-created"
+ className="organization-empty"
>
<h3
className="text-center"
diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.ts b/server/sonar-web/src/main/js/apps/organizations/routes.ts
index e3279292d9c..d78634091ec 100644
--- a/server/sonar-web/src/main/js/apps/organizations/routes.ts
+++ b/server/sonar-web/src/main/js/apps/organizations/routes.ts
@@ -34,11 +34,8 @@ const routes = [
{
indexRoute: {
onEnter(nextState: RouterState, replace: RedirectFunction) {
- const { location, params } = nextState;
- const justCreated = Boolean(location.state && location.state.justCreated);
- if (!justCreated) {
- replace(`/organizations/${params.organizationKey}/projects`);
- }
+ const { params } = nextState;
+ replace(`/organizations/${params.organizationKey}/projects`);
}
}
},
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
index 94f52fe4461..1c141b7e6f5 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
@@ -26,6 +26,7 @@ import PageSidebar from './PageSidebar';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import ListFooter from '../../../components/controls/ListFooter';
+import OrganizationEmpty from '../../organizations/components/OrganizationEmpty';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import Visualizations from '../visualizations/Visualizations';
@@ -33,11 +34,12 @@ import { Project, Facets } from '../types';
import { fetchProjects, parseSorting, SORTING_SWITCH } from '../utils';
import { parseUrlQuery, Query, hasFilterParams, hasVisualizationParams } from '../query';
import { translate } from '../../../helpers/l10n';
-import { addSideBarFooterClass, removeSideBarFooterClass } from '../../../helpers/pages';
+import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
import { RawQuery } from '../../../helpers/query';
import { get, save } from '../../../helpers/storage';
import { isSonarCloud } from '../../../helpers/system';
import { isLoggedIn } from '../../../helpers/users';
+import { OnboardingContext } from '../../../app/components/OnboardingContext';
import { withRouter, Location, Router } from '../../../components/hoc/withRouter';
import '../../../components/search-navigator.css';
import '../styles.css';
@@ -47,13 +49,13 @@ interface Props {
isFavorite: boolean;
location: Pick<Location, 'pathname' | 'query'>;
organization: T.Organization | undefined;
- organizationsEnabled?: boolean;
router: Pick<Router, 'push' | 'replace'>;
storageOptionsSuffix?: string;
}
interface State {
facets?: Facets;
+ initialLoading: boolean;
loading: boolean;
pageIndex?: number;
projects?: Project[];
@@ -70,7 +72,7 @@ export class AllProjects extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
- this.state = { loading: true, query: {} };
+ this.state = { initialLoading: true, loading: true, query: {} };
}
componentDidMount() {
@@ -81,38 +83,37 @@ export class AllProjects extends React.PureComponent<Props, State> {
return;
}
this.handleQueryChange(true);
- addSideBarFooterClass();
+ this.updateFooterClass();
}
componentDidUpdate(prevProps: Props) {
if (prevProps.location.query !== this.props.location.query) {
this.handleQueryChange(false);
}
+
+ if (
+ prevProps.organization &&
+ this.props.organization &&
+ prevProps.organization.key !== this.props.organization.key
+ ) {
+ this.setState({ initialLoading: true });
+ }
+
+ this.updateFooterClass();
}
componentWillUnmount() {
this.mounted = false;
- removeSideBarFooterClass();
+ removeSideBarClass();
}
- getView = () => this.state.query.view || 'overall';
-
- getVisualization = () => this.state.query.visualization || 'risk';
-
- getSort = () => this.state.query.sort || 'name';
-
- stopLoading = () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- };
-
fetchProjects = (query: any) => {
this.setState({ loading: true, query });
fetchProjects(query, this.props.isFavorite, this.props.organization).then(response => {
if (this.mounted) {
this.setState({
facets: response.facets,
+ initialLoading: false,
loading: false,
pageIndex: 1,
projects: response.projects,
@@ -141,6 +142,8 @@ export class AllProjects extends React.PureComponent<Props, State> {
}
};
+ getSort = () => this.state.query.sort || 'name';
+
getStorageOptions = () => {
const { storageOptionsSuffix } = this.props;
const options: {
@@ -160,6 +163,14 @@ export class AllProjects extends React.PureComponent<Props, State> {
return options;
};
+ getView = () => this.state.query.view || 'overall';
+
+ getVisualization = () => this.state.query.visualization || 'risk';
+
+ handleClearAll = () => {
+ this.props.router.push({ pathname: this.props.location.pathname });
+ };
+
handlePerspectiveChange = ({ view, visualization }: { view: string; visualization?: string }) => {
const { storageOptionsSuffix } = this.props;
const query: {
@@ -188,12 +199,6 @@ export class AllProjects extends React.PureComponent<Props, State> {
save(PROJECTS_VISUALIZATION, visualization, storageOptionsSuffix);
};
- handleSortChange = (sort: string, desc: boolean) => {
- const asString = (desc ? '-' : '') + sort;
- this.updateLocationQuery({ sort: asString });
- save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix);
- };
-
handleQueryChange(initialMount: boolean) {
const query = parseUrlQuery(this.props.location.query);
const savedOptions = this.getStorageOptions();
@@ -207,13 +212,34 @@ export class AllProjects extends React.PureComponent<Props, State> {
}
}
+ handleSortChange = (sort: string, desc: boolean) => {
+ const asString = (desc ? '-' : '') + sort;
+ this.updateLocationQuery({ sort: asString });
+ save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix);
+ };
+
+ stopLoading = () => {
+ if (this.mounted) {
+ this.setState({ initialLoading: false, loading: false });
+ }
+ };
+
updateLocationQuery = (newQuery: RawQuery) => {
const query = omitBy({ ...this.props.location.query, ...newQuery }, x => !x);
this.props.router.push({ pathname: this.props.location.pathname, query });
};
- handleClearAll = () => {
- this.props.router.push({ pathname: this.props.location.pathname });
+ updateFooterClass = () => {
+ const { organization } = this.props;
+ const { initialLoading, projects } = this.state;
+ const isOrganizationContext = isSonarCloud() && organization;
+ const isEmpty = projects && projects.length === 0;
+
+ if (isOrganizationContext && (initialLoading || isEmpty)) {
+ removeSideBarClass();
+ } else {
+ addSideBarClass();
+ }
};
renderSide = () => (
@@ -270,7 +296,7 @@ export class AllProjects extends React.PureComponent<Props, State> {
<div className="layout-page-main-inner">
{this.state.projects && (
<Visualizations
- displayOrganizations={!this.props.organization && this.props.organizationsEnabled}
+ displayOrganizations={!this.props.organization && isSonarCloud()}
projects={this.state.projects}
sort={this.state.query.sort}
total={this.state.total}
@@ -304,17 +330,44 @@ export class AllProjects extends React.PureComponent<Props, State> {
};
render() {
+ const { organization } = this.props;
+ const { projects } = this.state;
+ const isOrganizationContext = isSonarCloud() && organization;
+ const initialLoading = isOrganizationContext && this.state.initialLoading;
+ const organizationEmpty = isOrganizationContext && projects && projects.length === 0;
+
return (
<div className="layout-page projects-page" id="projects-page">
<Suggestions suggestions="projects" />
<Helmet title={translate('projects.page')} />
- {this.renderSide()}
+ {initialLoading ? (
+ <div className="display-flex-space-around width-100 huge-spacer-top">
+ <DeferredSpinner />
+ </div>
+ ) : (
+ <>
+ {!organizationEmpty && this.renderSide()}
- <div className="layout-page-main">
- {this.renderHeader()}
- {this.renderMain()}
- </div>
+ <div className="layout-page-main">
+ {organizationEmpty && organization ? (
+ <OnboardingContext.Consumer>
+ {openProjectOnboarding => (
+ <OrganizationEmpty
+ openProjectOnboarding={openProjectOnboarding}
+ organization={organization}
+ />
+ )}
+ </OnboardingContext.Consumer>
+ ) : (
+ <>
+ {this.renderHeader()}
+ {this.renderMain()}
+ </>
+ )}
+ </div>
+ </>
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
index 12e7e5d81e5..d1ac2e535f0 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
@@ -22,6 +22,8 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import { AllProjects } from '../AllProjects';
import { get, save } from '../../../../helpers/storage';
+import { isSonarCloud } from '../../../../helpers/system';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
jest.mock('../ProjectsList', () => ({
// eslint-disable-next-line
@@ -55,11 +57,14 @@ jest.mock('../../../../helpers/storage', () => ({
save: jest.fn()
}));
-const fetchProjects = require('../../utils').fetchProjects as jest.Mock<any>;
+jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
+
+const fetchProjects = require('../../utils').fetchProjects as jest.Mock;
beforeEach(() => {
- (get as jest.Mock<any>).mockImplementation(() => null);
- (save as jest.Mock<any>).mockClear();
+ (get as jest.Mock).mockImplementation(() => null);
+ (save as jest.Mock).mockClear();
+ (isSonarCloud as jest.Mock).mockReturnValue(false);
fetchProjects.mockClear();
});
@@ -100,7 +105,7 @@ it('fetches projects', () => {
});
it('redirects to the saved search', () => {
- (get as jest.Mock<any>).mockImplementation(
+ (get as jest.Mock).mockImplementation(
(key: string) => (key === 'sonarqube.projects.view' ? 'leak' : null)
);
const replace = jest.fn();
@@ -161,6 +166,28 @@ it('changes perspective to risk visualization', () => {
expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', 'risk', undefined);
});
+it('renders correctly empty organization', async () => {
+ (isSonarCloud as jest.Mock).mockReturnValue(true);
+ const wrapper = shallow(
+ <AllProjects
+ currentUser={{ isLoggedIn: true }}
+ isFavorite={false}
+ location={{ pathname: '/projects', query: {} }}
+ organization={{ key: 'foo', name: 'Foo' }}
+ router={{ push: jest.fn(), replace: jest.fn() }}
+ />
+ );
+ expect(wrapper).toMatchSnapshot();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({
+ loading: false,
+ projects: [{ key: 'foo', measures: {}, name: 'Foo' }],
+ total: 0
+ });
+ expect(wrapper).toMatchSnapshot();
+});
+
function shallowRender(
props: Partial<AllProjects['props']> = {},
push = jest.fn(),
@@ -172,7 +199,6 @@ function shallowRender(
isFavorite={false}
location={{ pathname: '/projects', query: {} }}
organization={undefined}
- organizationsEnabled={false}
router={{ push, replace }}
{...props}
/>
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 e0873b4d900..1c09c537190 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
@@ -231,3 +231,203 @@ exports[`renders 2`] = `
</div>
</div>
`;
+
+exports[`renders correctly empty organization 1`] = `
+<div
+ className="layout-page projects-page"
+ id="projects-page"
+>
+ <Suggestions
+ suggestions="projects"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="projects.page"
+ />
+ <div
+ className="display-flex-space-around width-100 huge-spacer-top"
+ >
+ <DeferredSpinner
+ timeout={100}
+ />
+ </div>
+</div>
+`;
+
+exports[`renders correctly empty organization 2`] = `
+<div
+ className="layout-page projects-page"
+ id="projects-page"
+>
+ <Suggestions
+ suggestions="projects"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="projects.page"
+ />
+ <div
+ className="layout-page-main"
+ >
+ <ContextConsumer>
+ <Component />
+ </ContextConsumer>
+ </div>
+</div>
+`;
+
+exports[`renders correctly empty organization 3`] = `
+<div
+ className="layout-page projects-page"
+ id="projects-page"
+>
+ <Suggestions
+ suggestions="projects"
+ />
+ <HelmetWrapper
+ defer={true}
+ encodeSpecialCharacters={true}
+ title="projects.page"
+ />
+ <ScreenPositionHelper
+ className="layout-page-side-outer"
+ >
+ <Component />
+ </ScreenPositionHelper>
+ <div
+ className="layout-page-main"
+ >
+ <div
+ className="layout-page-header-panel layout-page-main-header"
+ >
+ <div
+ className="layout-page-header-panel-inner layout-page-main-header-inner"
+ >
+ <div
+ className="layout-page-main-inner"
+ >
+ <PageHeader
+ currentUser={
+ Object {
+ "isLoggedIn": true,
+ }
+ }
+ isFavorite={false}
+ loading={false}
+ onPerspectiveChange={[Function]}
+ onQueryChange={[Function]}
+ onSortChange={[Function]}
+ organization={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ projects={
+ Array [
+ Object {
+ "key": "foo",
+ "measures": Object {},
+ "name": "Foo",
+ },
+ ]
+ }
+ query={
+ Object {
+ "coverage": undefined,
+ "duplications": undefined,
+ "gate": undefined,
+ "languages": undefined,
+ "maintainability": undefined,
+ "new_coverage": undefined,
+ "new_duplications": undefined,
+ "new_lines": undefined,
+ "new_maintainability": undefined,
+ "new_reliability": undefined,
+ "new_security": undefined,
+ "reliability": undefined,
+ "search": undefined,
+ "security": undefined,
+ "size": undefined,
+ "sort": undefined,
+ "tags": undefined,
+ "view": undefined,
+ "visualization": undefined,
+ }
+ }
+ selectedSort="name"
+ total={0}
+ view="overall"
+ visualization="risk"
+ />
+ </div>
+ </div>
+ </div>
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <div
+ className="layout-page-main-inner"
+ >
+ <ProjectsList
+ cardType="overall"
+ currentUser={
+ Object {
+ "isLoggedIn": true,
+ }
+ }
+ isFavorite={false}
+ isFiltered={false}
+ organization={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ projects={
+ Array [
+ Object {
+ "key": "foo",
+ "measures": Object {},
+ "name": "Foo",
+ },
+ ]
+ }
+ query={
+ Object {
+ "coverage": undefined,
+ "duplications": undefined,
+ "gate": undefined,
+ "languages": undefined,
+ "maintainability": undefined,
+ "new_coverage": undefined,
+ "new_duplications": undefined,
+ "new_lines": undefined,
+ "new_maintainability": undefined,
+ "new_reliability": undefined,
+ "new_security": undefined,
+ "reliability": undefined,
+ "search": undefined,
+ "security": undefined,
+ "size": undefined,
+ "sort": undefined,
+ "tags": undefined,
+ "view": undefined,
+ "visualization": undefined,
+ }
+ }
+ />
+ <ListFooter
+ count={1}
+ loadMore={[Function]}
+ ready={true}
+ total={0}
+ />
+ </div>
+ </DeferredSpinner>
+ </div>
+</div>
+`;