page.shouldHaveTotal(1);
}
+ @Test
+ public void should_open_default_page() {
+ // default page can be "All Projects" or "Favorite Projects" depending on your last choice
+ ProjectsPage page = nav.openProjects();
+
+ // all projects for anonymous user
+ page.shouldHaveTotal(2).shouldDisplayAllProjects();
+
+ // all projects by default for logged in user
+ page = nav.logIn().asAdmin().openProjects();
+ page.shouldHaveTotal(2).shouldDisplayAllProjects();
+
+ // select favorite
+ page.selectFavoriteProjects();
+ page = nav.openProjects();
+ page.shouldHaveTotal(0).shouldDisplayFavoriteProjects();
+
+ // select all
+ page.selectAllProjects();
+ page = nav.openProjects();
+ page.shouldHaveTotal(2).shouldDisplayAllProjects();
+ }
+
}
@Test
public void project_page() {
- nav.open("/projects");
+ nav.open("/dashboard?id=sample");
- $(By.linkText("Sample")).click();
$("#component-navigation-more").click();
$(By.linkText("Project Page")).click();
@Test
public void project_admin_page() {
- nav.logIn().asAdmin().open("/projects");
+ nav.logIn().asAdmin().open("/dashboard?id=sample");
- $(By.linkText("Sample")).click();
$("#component-navigation-admin").click();
$(By.linkText("Project Admin Page")).click();
import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Selenide.$$;
+import static com.codeborne.selenide.WebDriverRunner.url;
+import static org.assertj.core.api.Assertions.assertThat;
public class ProjectsPage {
$("#projects-total").shouldHave(text(String.valueOf(total)));
return this;
}
+
+ public ProjectsPage shouldDisplayAllProjects() {
+ assertThat(url()).endsWith("/projects");
+ return this;
+ }
+
+ public ProjectsPage shouldDisplayFavoriteProjects() {
+ assertThat(url()).endsWith("/projects/favorite");
+ return this;
+ }
+
+ public ProjectsPage selectAllProjects() {
+ $("#all-projects").click();
+ return shouldDisplayAllProjects();
+ }
+
+ public ProjectsPage selectFavoriteProjects() {
+ $("#favorite-projects").click();
+ return shouldDisplayFavoriteProjects();
+ }
}
<td>css=.js-user-authenticated</td>
<td></td>
</tr>
-<tr>
- <td>open</td>
- <td>/projects</td>
- <td></td>
-</tr>
-<tr>
- <td>waitForText</td>
- <td>css=.page-header</td>
- <td>*1 projects*</td>
-</tr>
-<tr>
- <td>assertTextPresent</td>
- <td>Sample</td>
- <td></td>
-</tr>
<tr>
<td>open</td>
<td>/project/deletion?id=sample</td>
</tr>
<tr>
<td>open</td>
- <td>/projects</td>
+ <td>/dashboard?id=sample</td>
<td></td>
</tr>
<tr>
- <td>waitForText</td>
- <td>css=.page-header</td>
- <td>*0 projects*</td>
-</tr>
-<tr>
- <td>assertTextNotPresent</td>
- <td>content</td>
- <td>*Sample*</td>
+ <td>waitForElementPresent</td>
+ <td>css=.process-spinner-failed</td>
+ <td></td>
</tr>
</tbody>
</table>
componentDidMount () {
const { currentUser, router } = this.props;
if (currentUser.isLoggedIn) {
- router.replace('/projects/favorite');
+ router.replace('/projects');
} else {
router.replace('/about');
}
}
render () {
- const homeController = this.props.currentUser.isLoggedIn ? '/projects/favorite' : '/about';
+ const homeController = this.props.currentUser.isLoggedIn ? '/projects' : '/about';
const homeLinkClassName = 'navbar-brand' + (this.props.customLogoUrl ? ' navbar-brand-custom' : '');
return (
<div className="navbar-header">
}
renderProjects () {
- const pathname = this.props.currentUser.isLoggedIn ? '/projects/favorite' : '/projects';
return (
<li>
- <Link to={{ pathname }} activeClassName="active">
+ <Link to="/projects" activeClassName="active">
{translate('projects.page')}
</Link>
</li>
activeClassName="active"
onlyActiveOnIndex={false}
style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- }
- }>
+ to="/projects">
projects.page
</Link>
</li>
<div id="projects-page" className="page page-limited">
<Helmet title={translate('projects.page')} titleTemplate="%s - SonarQube"/>
<PageHeaderContainer organization={this.props.organization}/>
- <AllProjectsContainer location={this.props.location} organization={this.props.organization}/>
+ <AllProjectsContainer
+ isFavorite={false}
+ location={this.props.location}
+ organization={this.props.organization}/>
</div>
);
}
import ProjectsListFooterContainer from './ProjectsListFooterContainer';
import PageSidebar from './PageSidebar';
import { parseUrlQuery } from '../store/utils';
+import { saveAll, saveFavorite } from '../utils';
export default class AllProjects extends React.Component {
static propTypes = {
};
componentDidMount () {
+ // do not touch organization-level page
+ if (!this.props.organization) {
+ if (this.props.isFavorite) {
+ saveFavorite();
+ } else {
+ saveAll();
+ }
+ }
this.handleQueryChange();
}
import { connect } from 'react-redux';
import AllProjects from './AllProjects';
import { fetchProjects } from '../store/actions';
-import { getCurrentUser } from '../../../store/rootReducer';
-const mapStateToProps = state => ({
- user: getCurrentUser(state),
- isFavorite: false
-});
-
-export default connect(
- mapStateToProps,
- { fetchProjects }
-)(AllProjects);
+export default connect(null, { fetchProjects })(AllProjects);
--- /dev/null
+/*
+ * 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 React from 'react';
+import { connect } from 'react-redux';
+import { withRouter } from 'react-router';
+import AllProjectsContainer from './AllProjectsContainer';
+import { getCurrentUser } from '../../../store/rootReducer';
+import { shouldRedirectToFavorite } from '../utils';
+
+class DefaultPageSelector extends React.PureComponent {
+ props: {
+ currentUser: { isLoggedIn: boolean },
+ location: {},
+ router: { replace: (path: string) => void }
+ };
+
+ componentDidMount () {
+ if (shouldRedirectToFavorite(this.props.currentUser)) {
+ this.props.router.replace('/projects/favorite');
+ }
+ }
+
+ render () {
+ if (shouldRedirectToFavorite(this.props.currentUser)) {
+ return null;
+ } else {
+ return (
+ <AllProjectsContainer
+ isFavorite={false}
+ location={this.props.location}
+ user={this.props.currentUser}/>
+ );
+ }
+ }
+}
+
+const mapStateToProps = state => ({
+ currentUser: getCurrentUser(state)
+});
+
+export default connect(mapStateToProps)(withRouter(DefaultPageSelector));
import React from 'react';
import { IndexLink, Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
+import { saveAll } from '../utils';
export default class FavoriteFilter extends React.Component {
render () {
return null;
}
- const pathnameForFavorite = this.props.organization ?
- `/organizations/${this.props.organization.key}/projects/favorite` :
- '/projects/favorite';
+ const pathnameForFavorite = this.props.organization
+ ? `/organizations/${this.props.organization.key}/projects/favorite`
+ : '/projects/favorite';
- const pathnameForAll = this.props.organization ?
- `/organizations/${this.props.organization.key}/projects` :
- '/projects';
+ const pathnameForAll = this.props.organization
+ ? `/organizations/${this.props.organization.key}/projects`
+ : '/projects';
return (
- <div className="projects-sidebar pull-left text-center">
- <div className="button-group">
- <Link to={pathnameForFavorite} className="button" activeClassName="button-active">
- {translate('my_favorites')}
- </Link>
- <IndexLink to={pathnameForAll} className="button" activeClassName="button-active">
- {translate('all')}
- </IndexLink>
- </div>
+ <div className="projects-sidebar pull-left text-center">
+ <div className="button-group">
+ <Link
+ id="favorite-projects"
+ to={pathnameForFavorite}
+ className="button"
+ activeClassName="button-active">
+ {translate('my_favorites')}
+ </Link>
+ <IndexLink
+ id="all-projects"
+ to={pathnameForAll}
+ className="button"
+ activeClassName="button-active"
+ onClick={saveAll}>
+ {translate('all')}
+ </IndexLink>
</div>
+ </div>
);
}
}
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import App from './components/App';
-import AllProjectsContainer from './components/AllProjectsContainer';
+import DefaultPageSelector from './components/DefaultPageSelector';
import FavoriteProjectsContainer from './components/FavoriteProjectsContainer';
export default (
- <Route component={App}>
- <IndexRoute component={AllProjectsContainer}/>
- <Route path="favorite" component={FavoriteProjectsContainer}/>
- </Route>
+ <Route component={App}>
+ <IndexRoute component={DefaultPageSelector}/>
+ <Route path="favorite" component={FavoriteProjectsContainer}/>
+ </Route>
);
--- /dev/null
+/*
+ * 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
+const LOCALSTORAGE_KEY = 'sonarqube.projects.default';
+const LOCALSTORAGE_FAVORITE = 'favorite';
+const LOCALSTORAGE_ALL = 'all';
+
+const isFavoriteSet = (): boolean => {
+ const setting = window.localStorage.getItem(LOCALSTORAGE_KEY);
+ return setting === LOCALSTORAGE_FAVORITE;
+};
+
+export const shouldRedirectToFavorite = (currentUser: { isLoggedIn: boolean }) => {
+ return currentUser.isLoggedIn && isFavoriteSet();
+};
+
+const save = (value: string) => {
+ try {
+ window.localStorage.setItem(LOCALSTORAGE_KEY, value);
+ } catch (e) {
+ // usually that means the storage is full
+ // just do nothing in this case
+ }
+};
+
+export const saveAll = () => save(LOCALSTORAGE_ALL);
+
+export const saveFavorite = () => save(LOCALSTORAGE_FAVORITE);