Browse Source

SONAR-10423 Allow homepage feature on SonarQube

tags/7.5
Julien Lancelot 6 years ago
parent
commit
b035b76e5e

+ 5
- 4
server/sonar-server/src/main/java/org/sonar/server/user/ws/CurrentAction.java View File

@@ -46,7 +46,6 @@ import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.Users.CurrentWsResponse.Permissions;
import static org.sonarqube.ws.Users.CurrentWsResponse.newBuilder;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.MY_PROJECTS;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.ORGANIZATION;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.PROJECT;
import static org.sonarqube.ws.client.user.UsersWsParameters.ACTION_CURRENT;
@@ -57,13 +56,15 @@ public class CurrentAction implements UsersWsAction {
private final DbClient dbClient;
private final DefaultOrganizationProvider defaultOrganizationProvider;
private final AvatarResolver avatarResolver;
private final HomepageTypes homepageTypes;

public CurrentAction(UserSession userSession, DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider,
AvatarResolver avatarResolver) {
AvatarResolver avatarResolver, HomepageTypes homepageTypes) {
this.userSession = userSession;
this.dbClient = dbClient;
this.defaultOrganizationProvider = defaultOrganizationProvider;
this.avatarResolver = avatarResolver;
this.homepageTypes = homepageTypes;
}

@Override
@@ -154,9 +155,9 @@ public class CurrentAction implements UsersWsAction {
}
}

private static CurrentWsResponse.Homepage defaultHomepage() {
private CurrentWsResponse.Homepage defaultHomepage() {
return CurrentWsResponse.Homepage.newBuilder()
.setType(MY_PROJECTS)
.setType(CurrentWsResponse.HomepageType.valueOf(homepageTypes.getDefaultType().name()))
.build();
}


server/sonar-server/src/main/java/org/sonar/server/user/ws/HomepageType.java → server/sonar-server/src/main/java/org/sonar/server/user/ws/HomepageTypes.java View File

@@ -21,14 +21,25 @@ package org.sonar.server.user.ws;

import java.util.List;

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
public interface HomepageTypes {

public enum HomepageType {
enum Type {
PROJECT,
/**
* This type in only available on SonarQube
*/
PROJECTS,
/**
* These types are only available on SonarCloud
*/
MY_PROJECTS, MY_ISSUES,
/**
* This type in only available when organizations are enabled
*/
ORGANIZATION
}

PROJECT, ORGANIZATION, MY_PROJECTS, MY_ISSUES;
List<Type> getTypes();

public static List<String> keys() {
return stream(values()).map(Enum::toString).collect(toList());
}
Type getDefaultType();
}

+ 89
- 0
server/sonar-server/src/main/java/org/sonar/server/user/ws/HomepageTypesImpl.java View File

@@ -0,0 +1,89 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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.
*/

package org.sonar.server.user.ws;

import java.util.EnumSet;
import java.util.List;
import org.sonar.api.Startable;
import org.sonar.api.config.Configuration;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.process.ProcessProperties;
import org.sonar.server.organization.OrganizationFlags;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_ISSUES;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_PROJECTS;
import static org.sonar.server.user.ws.HomepageTypes.Type.ORGANIZATION;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECT;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECTS;
import static org.sonar.server.user.ws.HomepageTypes.Type.values;

public class HomepageTypesImpl implements HomepageTypes, Startable {

private static final EnumSet<Type> ON_SONARQUBE = EnumSet.of(PROJECTS, PROJECT, ORGANIZATION);
private static final EnumSet<Type> ON_SONARCLOUD = EnumSet.of(PROJECT, MY_PROJECTS, MY_ISSUES, ORGANIZATION);

private final Configuration configuration;
private final OrganizationFlags organizationFlags;
private final DbClient dbClient;

private List<Type> types;
private Type defaultType;

public HomepageTypesImpl(Configuration configuration, OrganizationFlags organizationFlags, DbClient dbClient) {
this.configuration = configuration;
this.organizationFlags = organizationFlags;
this.dbClient = dbClient;
}

@Override
public List<Type> getTypes() {
checkState(types != null, "Homepage types have not been initialized yet");
return types;
}

@Override
public Type getDefaultType() {
checkState(types != null, "Homepage types have not been initialized yet");
return defaultType;
}

@Override
public void start() {
try (DbSession dbSession = dbClient.openSession(false)) {
boolean isOnSonarCloud = configuration.getBoolean(ProcessProperties.Property.SONARCLOUD_ENABLED.getKey()).orElse(false);
boolean isOrganizationEnabled = organizationFlags.isEnabled(dbSession);
this.types = stream(values())
.filter(type -> (isOnSonarCloud && ON_SONARCLOUD.contains(type)) || (!isOnSonarCloud && ON_SONARQUBE.contains(type)))
.filter(type -> isOrganizationEnabled || !(type.equals(ORGANIZATION)))
.collect(toList());
this.defaultType = isOnSonarCloud ? Type.MY_PROJECTS : PROJECTS;
}
}

@Override
public void stop() {
// Nothing to do
}
}

+ 8
- 8
server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java View File

@@ -37,10 +37,8 @@ import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.sonar.server.user.ws.HomepageType.ORGANIZATION;
import static org.sonar.server.user.ws.HomepageType.PROJECT;
import static org.sonar.server.user.ws.HomepageType.keys;
import static org.sonar.server.user.ws.HomepageType.valueOf;
import static org.sonar.server.user.ws.HomepageTypes.Type.ORGANIZATION;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECT;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;

@@ -56,11 +54,13 @@ public class SetHomepageAction implements UsersWsAction {
private final UserSession userSession;
private final DbClient dbClient;
private final ComponentFinder componentFinder;
private HomepageTypes homepageTypes;

public SetHomepageAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder) {
public SetHomepageAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, HomepageTypes homepageTypes) {
this.userSession = userSession;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
this.homepageTypes = homepageTypes;
}

@Override
@@ -77,7 +77,7 @@ public class SetHomepageAction implements UsersWsAction {
action.createParam(PARAM_TYPE)
.setDescription("Type of the requested page")
.setRequired(true)
.setPossibleValues(keys());
.setPossibleValues(homepageTypes.getTypes());

action.createParam(PARAM_ORGANIZATION)
.setDescription("Organization key. It should only be used when parameter '%s' is set to '%s'", PARAM_TYPE, ORGANIZATION)
@@ -100,7 +100,7 @@ public class SetHomepageAction implements UsersWsAction {
@Override
public void handle(Request request, Response response) throws Exception {
userSession.checkLoggedIn();
HomepageType type = valueOf(request.mandatoryParam(PARAM_TYPE));
HomepageTypes.Type type = request.mandatoryParamAsEnum(PARAM_TYPE, HomepageTypes.Type.class);
String componentParameter = request.param(PARAM_COMPONENT);
String organizationParameter = request.param(PARAM_ORGANIZATION);

@@ -120,7 +120,7 @@ public class SetHomepageAction implements UsersWsAction {
}

@CheckForNull
private String getHomepageParameter(DbSession dbSession, HomepageType type, @Nullable String componentParameter, @Nullable String branchParameter,
private String getHomepageParameter(DbSession dbSession, HomepageTypes.Type type, @Nullable String componentParameter, @Nullable String branchParameter,
@Nullable String organizationParameter) {
switch (type) {
case PROJECT:

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/user/ws/UsersWsModule.java View File

@@ -37,6 +37,7 @@ public class UsersWsModule extends Module {
UserPropertiesWs.class,
UserJsonWriter.class,
SkipOnboardingTutorialAction.class,
SetHomepageAction.class);
SetHomepageAction.class,
HomepageTypesImpl.class);
}
}

+ 13
- 3
server/sonar-server/src/test/java/org/sonar/server/user/ws/CurrentActionTest.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.server.user.ws;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -37,13 +38,14 @@ import org.sonarqube.ws.Users.CurrentWsResponse;

import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
import static org.sonar.db.permission.OrganizationPermission.SCAN;
import static org.sonar.db.user.GroupTesting.newGroupDto;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.MY_PROJECTS;

public class CurrentActionTest {
@Rule
@@ -55,7 +57,15 @@ public class CurrentActionTest {

private DbClient dbClient = db.getDbClient();
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private WsActionTester ws = new WsActionTester(new CurrentAction(userSessionRule, dbClient, defaultOrganizationProvider, new AvatarResolverImpl()));
private HomepageTypes homepageTypes = mock(HomepageTypes.class);

private WsActionTester ws = new WsActionTester(new CurrentAction(userSessionRule, dbClient, defaultOrganizationProvider, new AvatarResolverImpl(), homepageTypes));

@Before
public void setUp() {
when(homepageTypes.getDefaultType()).thenReturn(HomepageTypes.Type.MY_PROJECTS);
ws = new WsActionTester(new CurrentAction(userSessionRule, dbClient, defaultOrganizationProvider, new AvatarResolverImpl(), homepageTypes));
}

@Test
public void return_user_info() {
@@ -153,7 +163,7 @@ public class CurrentActionTest {

assertThat(response.getHomepage())
.extracting(CurrentWsResponse.Homepage::getType)
.containsExactly(MY_PROJECTS);
.containsExactly(CurrentWsResponse.HomepageType.MY_PROJECTS);
}

@Test

+ 104
- 0
server/sonar-server/src/test/java/org/sonar/server/user/ws/HomepageTypesImplTest.java View File

@@ -0,0 +1,104 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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.
*/

package org.sonar.server.user.ws;

import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.config.internal.MapSettings;
import org.sonar.db.DbTester;
import org.sonar.server.organization.TestOrganizationFlags;

import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_ISSUES;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_PROJECTS;
import static org.sonar.server.user.ws.HomepageTypes.Type.ORGANIZATION;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECT;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECTS;

public class HomepageTypesImplTest {

@Rule
public DbTester db = DbTester.create();

private MapSettings settings = new MapSettings();
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();

private HomepageTypesImpl underTest = new HomepageTypesImpl(settings.asConfig(), organizationFlags, db.getDbClient());

@Test
public void types_on_sonarcloud_and_organization_disabled() {
settings.setProperty("sonar.sonarcloud.enabled", true);
organizationFlags.setEnabled(false);

underTest.start();

assertThat(underTest.getTypes()).containsExactlyInAnyOrder(PROJECT, MY_PROJECTS, MY_ISSUES);
}

@Test
public void types_on_sonarcloud_and_organization_enabled() {
settings.setProperty("sonar.sonarcloud.enabled", true);
organizationFlags.setEnabled(true);

underTest.start();

assertThat(underTest.getTypes()).containsExactlyInAnyOrder(PROJECT, MY_PROJECTS, MY_ISSUES, ORGANIZATION);
}

@Test
public void types_on_sonarqube_and_organization_disabled() {
settings.setProperty("sonar.sonarcloud.enabled", false);
organizationFlags.setEnabled(false);

underTest.start();

assertThat(underTest.getTypes()).containsExactlyInAnyOrder(PROJECT, PROJECTS);
}

@Test
public void types_on_sonarqube_and_organization_enabled() {
settings.setProperty("sonar.sonarcloud.enabled", false);
organizationFlags.setEnabled(true);

underTest.start();

assertThat(underTest.getTypes()).containsExactlyInAnyOrder(PROJECT, PROJECTS, ORGANIZATION);
}

@Test
public void default_type_on_sonarcloud() {
settings.setProperty("sonar.sonarcloud.enabled", true);

underTest.start();

assertThat(underTest.getDefaultType()).isEqualTo(MY_PROJECTS);
}

@Test
public void default_type_on_sonarqube() {
settings.setProperty("sonar.sonarcloud.enabled", false);

underTest.start();

assertThat(underTest.getDefaultType()).isEqualTo(PROJECTS);
}

}

+ 17
- 3
server/sonar-server/src/test/java/org/sonar/server/user/ws/SetHomepageActionTest.java View File

@@ -19,6 +19,7 @@
*/
package org.sonar.server.user.ws;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -34,8 +35,15 @@ import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;

import static java.util.Arrays.asList;
import static org.apache.http.HttpStatus.SC_NO_CONTENT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_ISSUES;
import static org.sonar.server.user.ws.HomepageTypes.Type.MY_PROJECTS;
import static org.sonar.server.user.ws.HomepageTypes.Type.ORGANIZATION;
import static org.sonar.server.user.ws.HomepageTypes.Type.PROJECT;

public class SetHomepageActionTest {

@@ -49,8 +57,15 @@ public class SetHomepageActionTest {
public ExpectedException expectedException = ExpectedException.none();

private DbClient dbClient = db.getDbClient();
private SetHomepageAction underTest = new SetHomepageAction(userSession, dbClient, TestComponentFinder.from(db));
private WsActionTester ws = new WsActionTester(underTest);
private HomepageTypes homepageTypes = mock(HomepageTypes.class);

private WsActionTester ws;

@Before
public void setUp() {
when(homepageTypes.getTypes()).thenReturn(asList(PROJECT, ORGANIZATION, MY_ISSUES, MY_PROJECTS));
ws = new WsActionTester(new SetHomepageAction(userSession, dbClient, TestComponentFinder.from(db), homepageTypes));
}

@Test
public void verify_definition() {
@@ -61,7 +76,6 @@ public class SetHomepageActionTest {
assertThat(action.since()).isEqualTo("7.0");
assertThat(action.description()).isEqualTo("Set homepage of current user.<br> Requires authentication.");
assertThat(action.responseExample()).isNull();
assertThat(action.handler()).isSameAs(underTest);
assertThat(action.params()).hasSize(4);

WebService.Param typeParam = action.param("type");

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsModuleTest.java View File

@@ -29,6 +29,6 @@ public class UsersWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new UsersWsModule().configure(container);
assertThat(container.size()).isEqualTo(2 + 13);
assertThat(container.size()).isEqualTo(2 + 14);
}
}

+ 1
- 11
server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsTest.java View File

@@ -25,7 +25,6 @@ import org.junit.Test;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.DbClient;
import org.sonar.server.issue.ws.AvatarResolver;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.UserUpdater;
import org.sonar.server.user.index.UserIndex;
@@ -45,7 +44,6 @@ public class UsersWsTest {
WsTester tester = new WsTester(new UsersWs(
new CreateAction(mock(DbClient.class), mock(UserUpdater.class), userSessionRule),
new UpdateAction(mock(UserUpdater.class), userSessionRule, mock(UserJsonWriter.class), mock(DbClient.class)),
new CurrentAction(userSessionRule, mock(DbClient.class), mock(DefaultOrganizationProvider.class), mock(AvatarResolver.class)),
new ChangePasswordAction(mock(DbClient.class), mock(UserUpdater.class), userSessionRule),
new SearchAction(userSessionRule, mock(UserIndex.class), mock(DbClient.class), mock(AvatarResolver.class))));
controller = tester.controller("api/users");
@@ -56,7 +54,7 @@ public class UsersWsTest {
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("3.6");
assertThat(controller.actions()).hasSize(5);
assertThat(controller.actions()).hasSize(4);
}

@Test
@@ -92,12 +90,4 @@ public class UsersWsTest {
assertThat(action.params()).hasSize(3);
}

@Test
public void define_current_action() {
WebService.Action action = controller.action("current");
assertThat(action).isNotNull();
assertThat(action.isPost()).isFalse();
assertThat(action.isInternal()).isTrue();
assertThat(action.params()).isEmpty();
}
}

+ 1
- 0
sonar-ws/src/main/protobuf/ws-users.proto View File

@@ -119,6 +119,7 @@ message CurrentWsResponse {
ORGANIZATION = 2;
MY_PROJECTS = 3;
MY_ISSUES = 4;
PROJECTS = 5;
}

message Homepage {

+ 93
- 0
tests/src/test/java/org/sonarqube/tests/user/HomepageTest.java View File

@@ -0,0 +1,93 @@
/*
* SonarQube
* Copyright (C) 2009-2018 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.
*/

package org.sonarqube.tests.user;

import com.sonar.orchestrator.Orchestrator;
import javax.annotation.Nullable;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.qa.util.Tester;
import org.sonarqube.ws.Projects.CreateWsResponse.Project;
import org.sonarqube.ws.Users;
import org.sonarqube.ws.Users.CreateWsResponse.User;
import org.sonarqube.ws.Users.CurrentWsResponse.HomepageType;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.projects.DeleteRequest;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.PROJECT;
import static org.sonarqube.ws.Users.CurrentWsResponse.HomepageType.PROJECTS;

public class HomepageTest {

@ClassRule
public static final Orchestrator orchestrator = UserSuite.ORCHESTRATOR;

@Rule
public Tester tester = new Tester(orchestrator).disableOrganizations();

@Test
public void default_homepage() {
User user = tester.users().generate();

checkHomepage(user, PROJECTS, null);
}

@Test
public void set_and_get_homepage() {
Project project = tester.projects().provision();
User user = tester.users().generate();

setHomepage(user, "PROJECT", project.getKey());

checkHomepage(user, PROJECT, project);
}

@Test
public void fallback_to_projects_when_homepage_was_set_to_a_removed_project() {
User user = tester.users().generate();
Project project = tester.projects().provision();
setHomepage(user, "PROJECT", project.getKey());
checkHomepage(user, PROJECT, project);

tester.wsClient().projects().delete(new DeleteRequest().setProject(project.getKey()));

checkHomepage(user, PROJECTS, null);
}

private void setHomepage(User user, String type, @Nullable String component) {
tester.as(user.getLogin()).wsClient().wsConnector().call(new PostRequest("api/users/set_homepage")
.setParam("type", type)
.setParam("component", component))
.failIfNotSuccessful();
}

private void checkHomepage(User user, HomepageType type, @Nullable Project project) {
Users.CurrentWsResponse current = tester.as(user.getLogin()).wsClient().users().current();
assertThat(current.getHomepage().getType()).isEqualTo(type);
if (project != null) {
assertThat(current.getHomepage().getComponent()).isEqualTo(project.getKey());
} else {
assertThat(current.getHomepage().hasComponent()).isFalse();
}
}
}

+ 1
- 0
tests/src/test/java/org/sonarqube/tests/user/UserSuite.java View File

@@ -33,6 +33,7 @@ import static util.ItUtils.xooPlugin;
BaseIdentityProviderTest.class,
FavoritesWsTest.class,
ForceAuthenticationTest.class,
HomepageTest.class,
LocalAuthenticationTest.class,
MyAccountPageTest.class,
NotificationsWsTest.class,

Loading…
Cancel
Save