aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-05-10 18:13:28 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-05-12 11:58:12 +0200
commitaf0fb06721dcc88c53622f0380f1016aa0bd17c8 (patch)
tree4f722edeb8e2daf0aaa3f97781ccad3d886d10e0
parent9e7cede058c62b5785f5619b90d5e93331929871 (diff)
downloadsonarqube-af0fb06721dcc88c53622f0380f1016aa0bd17c8.tar.gz
sonarqube-af0fb06721dcc88c53622f0380f1016aa0bd17c8.zip
SONAR-9044 Easy access to my organizations
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js4
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js132
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.js35
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.js107
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap270
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js45
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js17
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/projects-admin/__tests__/projects-test.js16
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties1
10 files changed, 578 insertions, 57 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
index fca0171d9ec..86ed0974c5c 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
@@ -21,7 +21,7 @@ import React from 'react';
import { connect } from 'react-redux';
import GlobalNavBranding from './GlobalNavBranding';
import GlobalNavMenu from './GlobalNavMenu';
-import GlobalNavUser from './GlobalNavUser';
+import GlobalNavUserContainer from './GlobalNavUserContainer';
import Search from '../../search/Search';
import ShortcutsHelpView from './ShortcutsHelpView';
import { getCurrentUser, getAppState } from '../../../../store/rootReducer';
@@ -76,7 +76,7 @@ class GlobalNav extends React.PureComponent {
</svg>
</a>
</li>
- <GlobalNavUser {...this.props} />
+ <GlobalNavUserContainer {...this.props} />
</ul>
</div>
</nav>
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js
index 6d60902e1c8..ac0f288819b 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js
@@ -19,21 +19,47 @@
*/
// @flow
import React from 'react';
-import { Link, withRouter } from 'react-router';
+import classNames from 'classnames';
+import { sortBy } from 'lodash';
+import { Link } from 'react-router';
import Avatar from '../../../../components/ui/Avatar';
+import OrganizationLink from '../../../../components/ui/OrganizationLink';
import { translate } from '../../../../helpers/l10n';
-class GlobalNavUser extends React.PureComponent {
- props: {
- currentUser: {
- email?: string,
- name: string
- },
- location: Object,
- router: { push: string => void }
+type CurrentUser = {
+ email?: string,
+ isLoggedIn: boolean,
+ name: string
+};
+
+type Props = {
+ currentUser: CurrentUser,
+ fetchMyOrganizations: () => Promise<*>,
+ location: Object,
+ organizations: Array<{ key: string, name: string }>,
+ router: { push: string => void }
+};
+
+type State = {
+ open: boolean
+};
+
+export default class GlobalNavUser extends React.PureComponent {
+ node: HTMLElement;
+ props: Props;
+ state: State = { open: false };
+
+ componentWillUnmount() {
+ window.removeEventListener('click', this.handleClickOutside);
+ }
+
+ handleClickOutside = (event: { target: HTMLElement }) => {
+ if (!this.node || !this.node.contains(event.target)) {
+ this.closeDropdown();
+ }
};
- handleLogin = e => {
+ handleLogin = (e: Event) => {
e.preventDefault();
const shouldReturnToCurrentPage = window.location.pathname !== `${window.baseUrl}/about`;
if (shouldReturnToCurrentPage) {
@@ -45,36 +71,76 @@ class GlobalNavUser extends React.PureComponent {
}
};
- handleLogout = e => {
+ handleLogout = (e: Event) => {
e.preventDefault();
+ this.closeDropdown();
this.props.router.push('/sessions/logout');
};
+ toggleDropdown = (evt: Event) => {
+ evt.preventDefault();
+ if (this.state.open) {
+ this.closeDropdown();
+ } else {
+ this.openDropdown();
+ }
+ };
+
+ openDropdown = () => {
+ this.props.fetchMyOrganizations().then(() => {
+ window.addEventListener('click', this.handleClickOutside, true);
+ this.setState({ open: true });
+ });
+ };
+
+ closeDropdown = () => {
+ window.removeEventListener('click', this.handleClickOutside);
+ this.setState({ open: false });
+ };
+
renderAuthenticated() {
- const { currentUser } = this.props;
+ const { currentUser, organizations } = this.props;
+ const hasOrganizations = organizations.length > 0;
return (
- <li className="dropdown js-user-authenticated">
- <a className="dropdown-toggle navbar-avatar" data-toggle="dropdown" href="#">
+ <li
+ className={classNames('dropdown js-user-authenticated', { open: this.state.open })}
+ ref={node => (this.node = node)}>
+ <a className="dropdown-toggle navbar-avatar" href="#" onClick={this.toggleDropdown}>
<Avatar email={currentUser.email} name={currentUser.name} size={24} />
</a>
- <ul className="dropdown-menu dropdown-menu-right">
- <li className="dropdown-item">
- <div className="text-ellipsis text-muted" title={currentUser.name}>
- <strong>{currentUser.name}</strong>
- </div>
- {currentUser.email != null &&
- <div className="little-spacer-top text-ellipsis text-muted" title={currentUser.email}>
- {currentUser.email}
- </div>}
- </li>
- <li className="divider" />
- <li>
- <Link to="/account">{translate('my_account.page')}</Link>
- </li>
- <li>
- <a onClick={this.handleLogout} href="#">{translate('layout.logout')}</a>
- </li>
- </ul>
+ {this.state.open &&
+ <ul className="dropdown-menu dropdown-menu-right">
+ <li className="dropdown-item">
+ <div className="text-ellipsis text-muted" title={currentUser.name}>
+ <strong>{currentUser.name}</strong>
+ </div>
+ {currentUser.email != null &&
+ <div
+ className="little-spacer-top text-ellipsis text-muted"
+ title={currentUser.email}>
+ {currentUser.email}
+ </div>}
+ </li>
+ <li className="divider" />
+ <li>
+ <Link to="/account" onClick={this.closeDropdown}>{translate('my_account.page')}</Link>
+ </li>
+ {hasOrganizations && <li role="separator" className="divider" />}
+ {hasOrganizations &&
+ <li className="dropdown-header spacer-left">{translate('my_organizations')}</li>}
+ {hasOrganizations &&
+ sortBy(organizations, org => org.name.toLowerCase()).map(organization => (
+ <li key={organization.key}>
+ <OrganizationLink organization={organization} onClick={this.closeDropdown}>
+ <span className="spacer-left">{organization.name}</span>
+ </OrganizationLink>
+ </li>
+ ))}
+ {hasOrganizations && <li role="separator" className="divider" />}
+ <li>
+ <a onClick={this.handleLogout} href="#">{translate('layout.logout')}</a>
+ </li>
+ </ul>}
</li>
);
}
@@ -91,5 +157,3 @@ class GlobalNavUser extends React.PureComponent {
return this.props.currentUser.isLoggedIn ? this.renderAuthenticated() : this.renderAnonymous();
}
}
-
-export default withRouter(GlobalNavUser);
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.js
new file mode 100644
index 00000000000..c84bdff2a7a
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.js
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+// @flow
+import { withRouter } from 'react-router';
+import { connect } from 'react-redux';
+import GlobalNavUser from './GlobalNavUser';
+import { fetchMyOrganizations } from '../../../../apps/account/organizations/actions';
+import { getMyOrganizations } from '../../../../store/rootReducer';
+
+const mapStateToProps = state => ({
+ organizations: getMyOrganizations(state)
+});
+
+const mapDispatchToProps = {
+ fetchMyOrganizations
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(withRouter(GlobalNavUser));
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.js b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.js
new file mode 100644
index 00000000000..0a9f7fb9fa3
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.js
@@ -0,0 +1,107 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 React from 'react';
+import { shallow } from 'enzyme';
+import GlobalNavUser from '../GlobalNavUser';
+
+const currentUser = { isLoggedIn: true, name: 'foo', email: 'foo@bar.baz' };
+const organizations = [
+ { key: 'myorg', name: 'MyOrg' },
+ { key: 'foo', name: 'Foo' },
+ { key: 'bar', name: 'bar' }
+];
+
+it('should render the right interface for anonymous user', () => {
+ const currentUser = { isLoggedIn: false };
+ const wrapper = shallow(
+ <GlobalNavUser currentUser={currentUser} fetchMyOrganizations={() => {}} organizations={[]} />
+ );
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render the right interface for logged in user', () => {
+ const wrapper = shallow(
+ <GlobalNavUser currentUser={currentUser} fetchMyOrganizations={() => {}} organizations={[]} />
+ );
+ wrapper.setState({ open: true });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render the users organizations', () => {
+ const wrapper = shallow(
+ <GlobalNavUser
+ currentUser={currentUser}
+ fetchMyOrganizations={() => {}}
+ organizations={organizations}
+ />
+ );
+ wrapper.setState({ open: true });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should update the component correctly when the user changes to anonymous', () => {
+ const fetchMyOrganizations = jest.fn();
+ const wrapper = shallow(
+ <GlobalNavUser
+ currentUser={currentUser}
+ fetchMyOrganizations={fetchMyOrganizations}
+ organizations={[]}
+ />
+ );
+ wrapper.setState({ open: true });
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setProps({ currentUser: { isLoggedIn: false } });
+ expect(fetchMyOrganizations.mock.calls.length).toBe(0);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should lazyload the organizations when opening the dropdown', () => {
+ const fetchMyOrganizations = jest.fn(() => Promise.resolve());
+ const wrapper = shallow(
+ <GlobalNavUser
+ currentUser={currentUser}
+ fetchMyOrganizations={fetchMyOrganizations}
+ organizations={organizations}
+ />
+ );
+ expect(fetchMyOrganizations.mock.calls.length).toBe(0);
+ wrapper.instance().openDropdown();
+ expect(fetchMyOrganizations.mock.calls.length).toBe(1);
+ wrapper.instance().openDropdown();
+ expect(fetchMyOrganizations.mock.calls.length).toBe(2);
+});
+
+it('should update the organizations when the user changes', () => {
+ const fetchMyOrganizations = jest.fn(() => Promise.resolve());
+ const wrapper = shallow(
+ <GlobalNavUser
+ currentUser={currentUser}
+ fetchMyOrganizations={fetchMyOrganizations}
+ organizations={organizations}
+ />
+ );
+ wrapper.instance().openDropdown();
+ expect(fetchMyOrganizations.mock.calls.length).toBe(1);
+ wrapper.setProps({
+ currentUser: { isLoggedIn: true, name: 'test', email: 'test@sonarsource.com' }
+ });
+ wrapper.instance().openDropdown();
+ expect(fetchMyOrganizations.mock.calls.length).toBe(2);
+});
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap
new file mode 100644
index 00000000000..50f52bb687c
--- /dev/null
+++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap
@@ -0,0 +1,270 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render the right interface for anonymous user 1`] = `
+<li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ layout.login
+ </a>
+</li>
+`;
+
+exports[`should render the right interface for logged in user 1`] = `
+<li
+ className="dropdown js-user-authenticated open"
+>
+ <a
+ className="dropdown-toggle navbar-avatar"
+ href="#"
+ onClick={[Function]}
+ >
+ <Connect(Avatar)
+ email="foo@bar.baz"
+ name="foo"
+ size={24}
+ />
+ </a>
+ <ul
+ className="dropdown-menu dropdown-menu-right"
+ >
+ <li
+ className="dropdown-item"
+ >
+ <div
+ className="text-ellipsis text-muted"
+ title="foo"
+ >
+ <strong>
+ foo
+ </strong>
+ </div>
+ <div
+ className="little-spacer-top text-ellipsis text-muted"
+ title="foo@bar.baz"
+ >
+ foo@bar.baz
+ </div>
+ </li>
+ <li
+ className="divider"
+ />
+ <li>
+ <Link
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/account"
+ >
+ my_account.page
+ </Link>
+ </li>
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ layout.logout
+ </a>
+ </li>
+ </ul>
+</li>
+`;
+
+exports[`should render the users organizations 1`] = `
+<li
+ className="dropdown js-user-authenticated open"
+>
+ <a
+ className="dropdown-toggle navbar-avatar"
+ href="#"
+ onClick={[Function]}
+ >
+ <Connect(Avatar)
+ email="foo@bar.baz"
+ name="foo"
+ size={24}
+ />
+ </a>
+ <ul
+ className="dropdown-menu dropdown-menu-right"
+ >
+ <li
+ className="dropdown-item"
+ >
+ <div
+ className="text-ellipsis text-muted"
+ title="foo"
+ >
+ <strong>
+ foo
+ </strong>
+ </div>
+ <div
+ className="little-spacer-top text-ellipsis text-muted"
+ title="foo@bar.baz"
+ >
+ foo@bar.baz
+ </div>
+ </li>
+ <li
+ className="divider"
+ />
+ <li>
+ <Link
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/account"
+ >
+ my_account.page
+ </Link>
+ </li>
+ <li
+ className="divider"
+ role="separator"
+ />
+ <li
+ className="dropdown-header spacer-left"
+ >
+ my_organizations
+ </li>
+ <li>
+ <OrganizationLink
+ onClick={[Function]}
+ organization={
+ Object {
+ "key": "bar",
+ "name": "bar",
+ }
+ }
+ >
+ <span
+ className="spacer-left"
+ >
+ bar
+ </span>
+ </OrganizationLink>
+ </li>
+ <li>
+ <OrganizationLink
+ onClick={[Function]}
+ organization={
+ Object {
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ >
+ <span
+ className="spacer-left"
+ >
+ Foo
+ </span>
+ </OrganizationLink>
+ </li>
+ <li>
+ <OrganizationLink
+ onClick={[Function]}
+ organization={
+ Object {
+ "key": "myorg",
+ "name": "MyOrg",
+ }
+ }
+ >
+ <span
+ className="spacer-left"
+ >
+ MyOrg
+ </span>
+ </OrganizationLink>
+ </li>
+ <li
+ className="divider"
+ role="separator"
+ />
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ layout.logout
+ </a>
+ </li>
+ </ul>
+</li>
+`;
+
+exports[`should update the component correctly when the user changes to anonymous 1`] = `
+<li
+ className="dropdown js-user-authenticated open"
+>
+ <a
+ className="dropdown-toggle navbar-avatar"
+ href="#"
+ onClick={[Function]}
+ >
+ <Connect(Avatar)
+ email="foo@bar.baz"
+ name="foo"
+ size={24}
+ />
+ </a>
+ <ul
+ className="dropdown-menu dropdown-menu-right"
+ >
+ <li
+ className="dropdown-item"
+ >
+ <div
+ className="text-ellipsis text-muted"
+ title="foo"
+ >
+ <strong>
+ foo
+ </strong>
+ </div>
+ <div
+ className="little-spacer-top text-ellipsis text-muted"
+ title="foo@bar.baz"
+ >
+ foo@bar.baz
+ </div>
+ </li>
+ <li
+ className="divider"
+ />
+ <li>
+ <Link
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/account"
+ >
+ my_account.page
+ </Link>
+ </li>
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ layout.logout
+ </a>
+ </li>
+ </ul>
+</li>
+`;
+
+exports[`should update the component correctly when the user changes to anonymous 2`] = `
+<li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ layout.login
+ </a>
+</li>
+`;
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
index 739948592e3..fe3ad60da2e 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
@@ -31,34 +31,45 @@ type OwnProps = {
params: { organizationKey: string }
};
+type Props = {
+ children?: React.Element<*>,
+ location: Object,
+ organization: null | Organization,
+ params: { organizationKey: string },
+ fetchOrganization: string => Promise<*>
+};
+
class OrganizationPage extends React.PureComponent {
mounted: boolean;
-
- props: {
- children?: React.Element<*>,
- location: Object,
- organization: null | Organization,
- params: { organizationKey: string },
- fetchOrganization: string => Promise<*>
- };
-
- state = {
- loading: true
- };
+ props: Props;
+ state = { loading: true };
componentDidMount() {
this.mounted = true;
- this.props.fetchOrganization(this.props.params.organizationKey).then(() => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- });
+ this.updateOrganization(this.props.params.organizationKey);
+ }
+
+ componentWillReceiveProps(nextProps: Props) {
+ if (nextProps.params.organizationKey !== this.props.params.organizationKey) {
+ this.updateOrganization(nextProps.params.organizationKey);
+ }
}
componentWillUnmount() {
this.mounted = false;
}
+ updateOrganization = (organizationKey: string) => {
+ if (this.mounted) {
+ this.setState({ loading: true });
+ }
+ this.props.fetchOrganization(organizationKey).then(() => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ });
+ };
+
render() {
const { organization } = this.props;
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js
index 69afb1dc902..4ced6f1a369 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js
@@ -23,7 +23,7 @@ import { UnconnectedOrganizationPage } from '../OrganizationPage';
it('smoke test', () => {
const wrapper = shallow(
- <UnconnectedOrganizationPage>
+ <UnconnectedOrganizationPage params={{ organizationKey: 'foo' }}>
<div>hello</div>
</UnconnectedOrganizationPage>
);
@@ -36,10 +36,23 @@ it('smoke test', () => {
it('not found', () => {
const wrapper = shallow(
- <UnconnectedOrganizationPage>
+ <UnconnectedOrganizationPage params={{ organizationKey: 'foo' }}>
<div>hello</div>
</UnconnectedOrganizationPage>
);
wrapper.setState({ loading: false });
expect(wrapper).toMatchSnapshot();
});
+
+it('should correctly update when the organization changes', () => {
+ const fetchOrganization = jest.fn(() => Promise.resolve());
+ const wrapper = shallow(
+ <UnconnectedOrganizationPage
+ params={{ organizationKey: 'foo' }}
+ fetchOrganization={fetchOrganization}>
+ <div>hello</div>
+ </UnconnectedOrganizationPage>
+ );
+ wrapper.setProps({ params: { organizationKey: 'bar' } });
+ expect(fetchOrganization.mock.calls).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
index 6c8f73738d4..d9579e1bf4a 100644
--- a/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
@@ -2,6 +2,14 @@
exports[`not found 1`] = `<NotFound />`;
+exports[`should correctly update when the organization changes 1`] = `
+Array [
+ Array [
+ "bar",
+ ],
+]
+`;
+
exports[`smoke test 1`] = `null`;
exports[`smoke test 2`] = `
diff --git a/server/sonar-web/src/main/js/apps/projects-admin/__tests__/projects-test.js b/server/sonar-web/src/main/js/apps/projects-admin/__tests__/projects-test.js
index ebe90b8f947..e17e8668963 100644
--- a/server/sonar-web/src/main/js/apps/projects-admin/__tests__/projects-test.js
+++ b/server/sonar-web/src/main/js/apps/projects-admin/__tests__/projects-test.js
@@ -28,7 +28,14 @@ it('should render list of projects with no selection', () => {
{ id: '2', key: 'b', name: 'B', qualifier: 'TRK' }
];
- const result = shallow(<Projects projects={projects} selection={[]} refresh={jest.fn()} />);
+ const result = shallow(
+ <Projects
+ organization={{ key: 'foo' }}
+ projects={projects}
+ selection={[]}
+ refresh={jest.fn()}
+ />
+ );
expect(result.find('tr').length).toBe(2);
expect(result.find(Checkbox).filterWhere(n => n.prop('checked')).length).toBe(0);
});
@@ -41,7 +48,12 @@ it('should render list of projects with one selected', () => {
const selection = ['a'];
const result = shallow(
- <Projects projects={projects} selection={selection} refresh={jest.fn()} />
+ <Projects
+ organization={{ key: 'foo' }}
+ projects={projects}
+ selection={selection}
+ refresh={jest.fn()}
+ />
);
expect(result.find('tr').length).toBe(2);
expect(result.find(Checkbox).filterWhere(n => n.prop('checked')).length).toBe(1);
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index d09c631e58c..6dbc794bc24 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -254,6 +254,7 @@ logging_out=You're logging out, please wait...
manage=Manage
move_left=Move left
move_right=Move right
+my_organizations=My Organizations
new_issues=New issues
new_violations=New violations
new_window=New window