jms -> mq
This commit is contained in:
parent
70c4540c4a
commit
ab7d898b8e
12
test-harness/.gitignore
vendored
12
test-harness/.gitignore
vendored
@ -1,6 +1,6 @@
|
|||||||
.idea
|
.idea
|
||||||
target
|
target
|
||||||
*.iml
|
*.iml
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
# Test Harness
|
# Test Harness
|
||||||
|
|
||||||
This repo conatins Test Harness, code name "Hercules".
|
This repo conatins Test Harness, code name "Hercules".
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
<commons-configuration.version>1.6</commons-configuration.version>
|
<commons-configuration.version>1.6</commons-configuration.version>
|
||||||
<cxf.version>4.0.3</cxf.version>
|
<cxf.version>4.0.3</cxf.version>
|
||||||
<ibm.mq.version>9.4.5.0</ibm.mq.version>
|
<ibm.mq.version>9.4.5.0</ibm.mq.version>
|
||||||
<javax.jms.version>2.0.1</javax.jms.version>
|
|
||||||
<kafka.clients.version>3.7.0</kafka.clients.version>
|
<kafka.clients.version>3.7.0</kafka.clients.version>
|
||||||
<confluent.version>7.6.0</confluent.version>
|
<confluent.version>7.6.0</confluent.version>
|
||||||
<assertj.version>3.24.2</assertj.version>
|
<assertj.version>3.24.2</assertj.version>
|
||||||
@ -296,12 +295,6 @@
|
|||||||
<artifactId>com.ibm.mq.allclient</artifactId>
|
<artifactId>com.ibm.mq.allclient</artifactId>
|
||||||
<version>${ibm.mq.version}</version>
|
<version>${ibm.mq.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>javax.jms</groupId>
|
|
||||||
<artifactId>javax.jms-api</artifactId>
|
|
||||||
<version>${javax.jms.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Kafka dependencies -->
|
<!-- Kafka dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.kafka</groupId>
|
<groupId>org.apache.kafka</groupId>
|
||||||
|
|||||||
@ -1,418 +1,418 @@
|
|||||||
package cz.moneta.test.harness;
|
package cz.moneta.test.harness;
|
||||||
|
|
||||||
import cz.moneta.test.harness.annotations.*;
|
import cz.moneta.test.harness.annotations.*;
|
||||||
import cz.moneta.test.harness.config.ConfigProvider;
|
import cz.moneta.test.harness.config.ConfigProvider;
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.context.BaseStoreAccessor;
|
import cz.moneta.test.harness.context.BaseStoreAccessor;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
import cz.moneta.test.harness.endpoints.MobileEndpoint;
|
import cz.moneta.test.harness.endpoints.MobileEndpoint;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import cz.moneta.test.harness.endpoints.greenscreen.GreenScreenEndpoint;
|
import cz.moneta.test.harness.endpoints.greenscreen.GreenScreenEndpoint;
|
||||||
import cz.moneta.test.harness.endpoints.jira.JiraTestResultPublisher;
|
import cz.moneta.test.harness.endpoints.jira.JiraTestResultPublisher;
|
||||||
import cz.moneta.test.harness.exception.BeforeAllHarnessException;
|
import cz.moneta.test.harness.exception.BeforeAllHarnessException;
|
||||||
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||||
import cz.moneta.test.harness.support.auth.Credentials;
|
import cz.moneta.test.harness.support.auth.Credentials;
|
||||||
import cz.moneta.test.harness.support.auth.Key;
|
import cz.moneta.test.harness.support.auth.Key;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.junit.jupiter.api.extension.*;
|
import org.junit.jupiter.api.extension.*;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
||||||
import org.junit.platform.engine.UniqueId;
|
import org.junit.platform.engine.UniqueId;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Parameter;
|
import java.lang.reflect.Parameter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class HarnessJunit5Extension implements BeforeAllCallback, BeforeEachCallback, ParameterResolver,
|
public class HarnessJunit5Extension implements BeforeAllCallback, BeforeEachCallback, ParameterResolver,
|
||||||
TestExecutionExceptionHandler, AfterEachCallback, ExecutionCondition, LifecycleMethodExecutionExceptionHandler,
|
TestExecutionExceptionHandler, AfterEachCallback, ExecutionCondition, LifecycleMethodExecutionExceptionHandler,
|
||||||
BeforeTestExecutionCallback, AfterAllCallback {
|
BeforeTestExecutionCallback, AfterAllCallback {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(HarnessJunit5Extension.class);
|
private static final Logger logger = LogManager.getLogger(HarnessJunit5Extension.class);
|
||||||
|
|
||||||
public static final String ACTIVE_ENDPOINTS = "ACTIVE_ENDPOINTS";
|
public static final String ACTIVE_ENDPOINTS = "ACTIVE_ENDPOINTS";
|
||||||
private static final Namespace ENDPOINT_NAMESPACE = Namespace.create("ENDPOINT");
|
private static final Namespace ENDPOINT_NAMESPACE = Namespace.create("ENDPOINT");
|
||||||
protected static final Namespace CONFIG_NAMESPACE = Namespace.create("CONFIG");
|
protected static final Namespace CONFIG_NAMESPACE = Namespace.create("CONFIG");
|
||||||
protected static final Namespace GENERATORS_NAMESPACE = Namespace.create("GENERATORS");
|
protected static final Namespace GENERATORS_NAMESPACE = Namespace.create("GENERATORS");
|
||||||
|
|
||||||
private static final String RESOLUTION_PASS = "PASS";
|
private static final String RESOLUTION_PASS = "PASS";
|
||||||
private static final String RESOLUTION_FAIL = "FAIL";
|
private static final String RESOLUTION_FAIL = "FAIL";
|
||||||
|
|
||||||
private static final String BROWSER_CONFIG_KEY = "browser";
|
private static final String BROWSER_CONFIG_KEY = "browser";
|
||||||
|
|
||||||
private boolean hasBeforeAllFail = false;
|
private boolean hasBeforeAllFail = false;
|
||||||
private String beforeAllThrowableMessage;
|
private String beforeAllThrowableMessage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeAll(ExtensionContext extensionContext) {
|
public void beforeAll(ExtensionContext extensionContext) {
|
||||||
extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet<Class<? extends Endpoint>>());
|
extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet<Class<? extends Endpoint>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeEach(ExtensionContext extensionContext) {
|
public void beforeEach(ExtensionContext extensionContext) {
|
||||||
extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet<Class<? extends Endpoint>>());
|
extensionContext.getStore(ENDPOINT_NAMESPACE).put(ACTIVE_ENDPOINTS, new CopyOnWriteArraySet<Class<? extends Endpoint>>());
|
||||||
extensionContext.getStore(ExtensionContext.Namespace.create("UPLOADS")).remove("jira.uploads");
|
extensionContext.getStore(ExtensionContext.Namespace.create("UPLOADS")).remove("jira.uploads");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
|
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
|
||||||
return Stream.<Supplier<Optional<?>>>of(
|
return Stream.<Supplier<Optional<?>>>of(
|
||||||
() -> getTestContext(parameterContext),
|
() -> getTestContext(parameterContext),
|
||||||
() -> getAuthKey(parameterContext))
|
() -> getAuthKey(parameterContext))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.anyMatch(Optional::isPresent);
|
.anyMatch(Optional::isPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
|
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
|
||||||
storeCurrentTestName(extensionContext);
|
storeCurrentTestName(extensionContext);
|
||||||
|
|
||||||
return Stream.<Supplier<Optional<?>>>of(
|
return Stream.<Supplier<Optional<?>>>of(
|
||||||
() -> getTestContext(parameterContext).map(tc -> {
|
() -> getTestContext(parameterContext).map(tc -> {
|
||||||
try {
|
try {
|
||||||
return tc.getType().getConstructor(Store.class, Store.class, Store.class, Store.class, Store.class)
|
return tc.getType().getConstructor(Store.class, Store.class, Store.class, Store.class, Store.class)
|
||||||
.newInstance(
|
.newInstance(
|
||||||
extensionContext.getRoot().getStore(Namespace.GLOBAL),
|
extensionContext.getRoot().getStore(Namespace.GLOBAL),
|
||||||
extensionContext.getStore(Namespace.GLOBAL),
|
extensionContext.getStore(Namespace.GLOBAL),
|
||||||
extensionContext.getStore(ENDPOINT_NAMESPACE),
|
extensionContext.getStore(ENDPOINT_NAMESPACE),
|
||||||
extensionContext.getStore(CONFIG_NAMESPACE),
|
extensionContext.getStore(CONFIG_NAMESPACE),
|
||||||
extensionContext.getStore(GENERATORS_NAMESPACE));
|
extensionContext.getStore(GENERATORS_NAMESPACE));
|
||||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||||
throw new ParameterResolutionException("Failed to initialize parameter " +
|
throw new ParameterResolutionException("Failed to initialize parameter " +
|
||||||
parameterContext.getParameter().getType().getSimpleName(), e);
|
parameterContext.getParameter().getType().getSimpleName(), e);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
() -> getAuthKey(parameterContext)
|
() -> getAuthKey(parameterContext)
|
||||||
.map(ak -> ak.getAnnotation(Key.class).value())
|
.map(ak -> ak.getAnnotation(Key.class).value())
|
||||||
.map(ak -> AuthSupport.getCredentials(ak, new BaseStoreAccessor(
|
.map(ak -> AuthSupport.getCredentials(ak, new BaseStoreAccessor(
|
||||||
extensionContext.getRoot().getStore(Namespace.GLOBAL),
|
extensionContext.getRoot().getStore(Namespace.GLOBAL),
|
||||||
extensionContext.getStore(Namespace.GLOBAL),
|
extensionContext.getStore(Namespace.GLOBAL),
|
||||||
extensionContext.getStore(ENDPOINT_NAMESPACE),
|
extensionContext.getStore(ENDPOINT_NAMESPACE),
|
||||||
extensionContext.getStore(CONFIG_NAMESPACE),
|
extensionContext.getStore(CONFIG_NAMESPACE),
|
||||||
extensionContext.getStore(GENERATORS_NAMESPACE)) {
|
extensionContext.getStore(GENERATORS_NAMESPACE)) {
|
||||||
})))
|
})))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new ParameterResolutionException("Parameter of type " +
|
.orElseThrow(() -> new ParameterResolutionException("Parameter of type " +
|
||||||
parameterContext.getParameter().getType().getSimpleName() +
|
parameterContext.getParameter().getType().getSimpleName() +
|
||||||
" is not supported"));
|
" is not supported"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleTestExecutionException(ExtensionContext extensionContext, Throwable throwable) throws Throwable {
|
public void handleTestExecutionException(ExtensionContext extensionContext, Throwable throwable) throws Throwable {
|
||||||
processException(extensionContext);
|
processException(extensionContext);
|
||||||
|
|
||||||
throw Optional.of(throwable)
|
throw Optional.of(throwable)
|
||||||
.filter(HarnessException.class::isInstance)
|
.filter(HarnessException.class::isInstance)
|
||||||
.map(AssertionError::new)
|
.map(AssertionError::new)
|
||||||
.map(Throwable.class::cast)
|
.map(Throwable.class::cast)
|
||||||
.orElse(throwable);
|
.orElse(throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleBeforeAllMethodExecutionException(ExtensionContext extensionContext, Throwable throwable) {
|
public void handleBeforeAllMethodExecutionException(ExtensionContext extensionContext, Throwable throwable) {
|
||||||
//This is workaround for possible bug in JUnit5 and Surefire - in case of beforeAll exception are test skipped instead of failed
|
//This is workaround for possible bug in JUnit5 and Surefire - in case of beforeAll exception are test skipped instead of failed
|
||||||
//https://github.com/junit-team/junit5/issues/2178
|
//https://github.com/junit-team/junit5/issues/2178
|
||||||
hasBeforeAllFail = true;
|
hasBeforeAllFail = true;
|
||||||
beforeAllThrowableMessage = throwable.toString();
|
beforeAllThrowableMessage = throwable.toString();
|
||||||
|
|
||||||
processException(extensionContext);
|
processException(extensionContext);
|
||||||
JiraTestResultPublisher.logJiraTestResult(extensionContext, throwable);
|
JiraTestResultPublisher.logJiraTestResult(extensionContext, throwable);
|
||||||
logger.error("Method @BeforeAll in test class {} failed with exception: {}.", () -> extensionContext.getRequiredTestClass().getName(), () -> beforeAllThrowableMessage);
|
logger.error("Method @BeforeAll in test class {} failed with exception: {}.", () -> extensionContext.getRequiredTestClass().getName(), () -> beforeAllThrowableMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTestExecution(ExtensionContext extensionContext) {
|
public void beforeTestExecution(ExtensionContext extensionContext) {
|
||||||
if (hasBeforeAllFail) {
|
if (hasBeforeAllFail) {
|
||||||
throw new BeforeAllHarnessException("Exception in before all occurred. Original exception message: " + beforeAllThrowableMessage);
|
throw new BeforeAllHarnessException("Exception in before all occurred. Original exception message: " + beforeAllThrowableMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processException(ExtensionContext extensionContext) {
|
private void processException(ExtensionContext extensionContext) {
|
||||||
Set<String> files = processWebEndpoints(extensionContext);
|
Set<String> files = processWebEndpoints(extensionContext);
|
||||||
processMobileEndpoints(extensionContext);
|
processMobileEndpoints(extensionContext);
|
||||||
processGreenscreenEndpoints(extensionContext);
|
processGreenscreenEndpoints(extensionContext);
|
||||||
|
|
||||||
Store uploads = extensionContext.getStore(Namespace.create("UPLOADS"));
|
Store uploads = extensionContext.getStore(Namespace.create("UPLOADS"));
|
||||||
uploads.put("jira.uploads", files);
|
uploads.put("jira.uploads", files);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processGreenscreenEndpoints(ExtensionContext extensionContext) {
|
private void processGreenscreenEndpoints(ExtensionContext extensionContext) {
|
||||||
getActiveEndpoints(extensionContext).stream()
|
getActiveEndpoints(extensionContext).stream()
|
||||||
.filter(p -> GreenScreenEndpoint.class.isAssignableFrom(p.getLeft()))
|
.filter(p -> GreenScreenEndpoint.class.isAssignableFrom(p.getLeft()))
|
||||||
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, GreenScreenEndpoint.class))
|
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, GreenScreenEndpoint.class))
|
||||||
.forEach(e -> logger.info("Failed on screen:\n" + e.getText(1, 1, 0)));
|
.forEach(e -> logger.info("Failed on screen:\n" + e.getText(1, 1, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processMobileEndpoints(ExtensionContext extensionContext) {
|
private void processMobileEndpoints(ExtensionContext extensionContext) {
|
||||||
getActiveEndpoints(extensionContext).stream()
|
getActiveEndpoints(extensionContext).stream()
|
||||||
.filter(p -> MobileEndpoint.class.isAssignableFrom(p.getLeft()))
|
.filter(p -> MobileEndpoint.class.isAssignableFrom(p.getLeft()))
|
||||||
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, MobileEndpoint.class))
|
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, MobileEndpoint.class))
|
||||||
.forEach(mobileEndpoint -> {
|
.forEach(mobileEndpoint -> {
|
||||||
mobileEndpoint.takeSnapshot(extensionContext.getDisplayName());
|
mobileEndpoint.takeSnapshot(extensionContext.getDisplayName());
|
||||||
mobileEndpoint.saveSources(extensionContext.getDisplayName());
|
mobileEndpoint.saveSources(extensionContext.getDisplayName());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> processWebEndpoints(ExtensionContext extensionContext) {
|
private Set<String> processWebEndpoints(ExtensionContext extensionContext) {
|
||||||
return getActiveEndpoints(extensionContext).stream()
|
return getActiveEndpoints(extensionContext).stream()
|
||||||
.filter(p -> WebEndpoint.class.isAssignableFrom(p.getLeft()))
|
.filter(p -> WebEndpoint.class.isAssignableFrom(p.getLeft()))
|
||||||
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, WebEndpoint.class))
|
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).get(c, WebEndpoint.class))
|
||||||
.map(webEndpoint -> {
|
.map(webEndpoint -> {
|
||||||
Set<String> files = new HashSet<>();
|
Set<String> files = new HashSet<>();
|
||||||
String filePrefix = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
|
String filePrefix = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
|
||||||
files.add(webEndpoint.takeSnapshot(filePrefix));
|
files.add(webEndpoint.takeSnapshot(filePrefix));
|
||||||
files.addAll(webEndpoint.captureLogs(filePrefix));
|
files.addAll(webEndpoint.captureLogs(filePrefix));
|
||||||
webEndpoint.captureDom(filePrefix);
|
webEndpoint.captureDom(filePrefix);
|
||||||
return files;
|
return files;
|
||||||
})
|
})
|
||||||
.filter(set -> !set.isEmpty())
|
.filter(set -> !set.isEmpty())
|
||||||
.collect(HashSet::new, Set::addAll, Set::addAll);
|
.collect(HashSet::new, Set::addAll, Set::addAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Set<Pair<Class<? extends Endpoint>, Object[]>> getActiveEndpoints(ExtensionContext extensionContext) {
|
private Set<Pair<Class<? extends Endpoint>, Object[]>> getActiveEndpoints(ExtensionContext extensionContext) {
|
||||||
return extensionContext.getStore(ENDPOINT_NAMESPACE).get(ACTIVE_ENDPOINTS, Set.class);
|
return extensionContext.getStore(ENDPOINT_NAMESPACE).get(ACTIVE_ENDPOINTS, Set.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeActiveEndpoints(ExtensionContext extensionContext) {
|
private void closeActiveEndpoints(ExtensionContext extensionContext) {
|
||||||
getActiveEndpoints(extensionContext).stream()
|
getActiveEndpoints(extensionContext).stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).remove(c, Endpoint.class))
|
.map(c -> extensionContext.getStore(ENDPOINT_NAMESPACE).remove(c, Endpoint.class))
|
||||||
.peek(e -> {
|
.peek(e -> {
|
||||||
if (e instanceof MobileEndpoint) {
|
if (e instanceof MobileEndpoint) {
|
||||||
((MobileEndpoint) e).captureVideo(extensionContext.getDisplayName());
|
((MobileEndpoint) e).captureVideo(extensionContext.getDisplayName());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.forEach(Endpoint::close);
|
.forEach(Endpoint::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterEach(ExtensionContext extensionContext) {
|
public void afterEach(ExtensionContext extensionContext) {
|
||||||
closeActiveEndpoints(extensionContext);
|
closeActiveEndpoints(extensionContext);
|
||||||
if (!hasBeforeAllFail) {
|
if (!hasBeforeAllFail) {
|
||||||
JiraTestResultPublisher.logJiraTestResult(extensionContext);
|
JiraTestResultPublisher.logJiraTestResult(extensionContext);
|
||||||
}
|
}
|
||||||
logTestResult(extensionContext);
|
logTestResult(extensionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterAll(ExtensionContext extensionContext) {
|
public void afterAll(ExtensionContext extensionContext) {
|
||||||
//Closes only endpoints initialized in BeforeAll and not used in test methods
|
//Closes only endpoints initialized in BeforeAll and not used in test methods
|
||||||
closeActiveEndpoints(extensionContext);
|
closeActiveEndpoints(extensionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Parameter> getAuthKey(ParameterContext parameterContext) {
|
private Optional<Parameter> getAuthKey(ParameterContext parameterContext) {
|
||||||
return Optional.of(parameterContext.getParameter())
|
return Optional.of(parameterContext.getParameter())
|
||||||
.filter(p -> p.getAnnotation(Key.class) != null)
|
.filter(p -> p.getAnnotation(Key.class) != null)
|
||||||
.filter(p -> Credentials.class.isAssignableFrom(p.getType()));
|
.filter(p -> Credentials.class.isAssignableFrom(p.getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Parameter> getTestContext(ParameterContext parameterContext) {
|
private Optional<Parameter> getTestContext(ParameterContext parameterContext) {
|
||||||
return Optional.of(parameterContext.getParameter())
|
return Optional.of(parameterContext.getParameter())
|
||||||
.filter(p -> p.getType().getAnnotation(TestContext.class) != null)
|
.filter(p -> p.getType().getAnnotation(TestContext.class) != null)
|
||||||
.filter(p -> StoreAccessor.class.isAssignableFrom(p.getType()));
|
.filter(p -> StoreAccessor.class.isAssignableFrom(p.getType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
|
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
|
||||||
if (isClassExtensionContext(context)) {
|
if (isClassExtensionContext(context)) {
|
||||||
return evaluateClassExecutionContext(context);
|
return evaluateClassExecutionContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment environment = getCurrentEnvironment(context);
|
Environment environment = getCurrentEnvironment(context);
|
||||||
return context.getTestMethod()
|
return context.getTestMethod()
|
||||||
.filter(m -> environment != null)
|
.filter(m -> environment != null)
|
||||||
.flatMap(m -> Stream.<Supplier<Optional<ConditionEvaluationResult>>>of(
|
.flatMap(m -> Stream.<Supplier<Optional<ConditionEvaluationResult>>>of(
|
||||||
() -> checkTestCaseEnvironments(environment, m, context.getDisplayName()),
|
() -> checkTestCaseEnvironments(environment, m, context.getDisplayName()),
|
||||||
() -> checkDefectEnvironments(environment, m, context.getDisplayName(), context),
|
() -> checkDefectEnvironments(environment, m, context.getDisplayName(), context),
|
||||||
() -> checkGlobalParameterSet(context, m, context.getDisplayName()),
|
() -> checkGlobalParameterSet(context, m, context.getDisplayName()),
|
||||||
() -> checkJiraReportingRules(context, m, context.getDisplayName()))
|
() -> checkJiraReportingRules(context, m, context.getDisplayName()))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.orElse(enableEvaluationAndLogExecutionStart(context));
|
.orElse(enableEvaluationAndLogExecutionStart(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isClassExtensionContext(ExtensionContext extensionContext) {
|
private boolean isClassExtensionContext(ExtensionContext extensionContext) {
|
||||||
String simpleTestName = extensionContext.getTestClass()
|
String simpleTestName = extensionContext.getTestClass()
|
||||||
.get()
|
.get()
|
||||||
.getSimpleName();
|
.getSimpleName();
|
||||||
|
|
||||||
return !extensionContext.getTestMethod().isPresent() && extensionContext.getDisplayName().equals(simpleTestName);
|
return !extensionContext.getTestMethod().isPresent() && extensionContext.getDisplayName().equals(simpleTestName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionEvaluationResult evaluateClassExecutionContext(ExtensionContext extensionContext) {
|
private ConditionEvaluationResult evaluateClassExecutionContext(ExtensionContext extensionContext) {
|
||||||
ConfigProvider.readConfig().forEach((k, v) -> extensionContext.getStore(CONFIG_NAMESPACE).put(k, v));
|
ConfigProvider.readConfig().forEach((k, v) -> extensionContext.getStore(CONFIG_NAMESPACE).put(k, v));
|
||||||
Browser definedBrowser = getBrowserFromConfig(extensionContext);
|
Browser definedBrowser = getBrowserFromConfig(extensionContext);
|
||||||
String className = getClassCanonicalName(extensionContext);
|
String className = getClassCanonicalName(extensionContext);
|
||||||
Class<?> testClass = extensionContext.getTestClass().get();
|
Class<?> testClass = extensionContext.getTestClass().get();
|
||||||
|
|
||||||
return Stream.<Supplier<Optional<Browser[]>>>of(
|
return Stream.<Supplier<Optional<Browser[]>>>of(
|
||||||
() -> Optional.ofNullable(testClass.getAnnotation(TestScenario.class))
|
() -> Optional.ofNullable(testClass.getAnnotation(TestScenario.class))
|
||||||
.map(TestScenario::browsers),
|
.map(TestScenario::browsers),
|
||||||
() -> Optional.ofNullable(testClass.getAnnotation(NonConcurrentTestScenario.class))
|
() -> Optional.ofNullable(testClass.getAnnotation(NonConcurrentTestScenario.class))
|
||||||
.map(NonConcurrentTestScenario::browsers),
|
.map(NonConcurrentTestScenario::browsers),
|
||||||
() -> Optional.ofNullable(testClass.getAnnotation(TestScenarioWithOrder.class))
|
() -> Optional.ofNullable(testClass.getAnnotation(TestScenarioWithOrder.class))
|
||||||
.map(TestScenarioWithOrder::browsers))
|
.map(TestScenarioWithOrder::browsers))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.filter(browsers -> Arrays.stream(browsers)
|
.filter(browsers -> Arrays.stream(browsers)
|
||||||
.noneMatch(definedBrowser::equals))
|
.noneMatch(definedBrowser::equals))
|
||||||
.map(browsers -> ConditionEvaluationResult.disabled("Tests in class " +
|
.map(browsers -> ConditionEvaluationResult.disabled("Tests in class " +
|
||||||
className + " disabled. It is marked to only run on " +
|
className + " disabled. It is marked to only run on " +
|
||||||
Arrays.stream(browsers)
|
Arrays.stream(browsers)
|
||||||
.map(Enum::name)
|
.map(Enum::name)
|
||||||
.collect(Collectors.joining(", "))))
|
.collect(Collectors.joining(", "))))
|
||||||
.orElse(enableClassForTests(className));
|
.orElse(enableClassForTests(className));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionEvaluationResult enableClassForTests(String className) {
|
private ConditionEvaluationResult enableClassForTests(String className) {
|
||||||
logger.info("Test execution in test class {} started.", () -> className);
|
logger.info("Test execution in test class {} started.", () -> className);
|
||||||
return ConditionEvaluationResult.enabled("Tests in " + className + " enabled.");
|
return ConditionEvaluationResult.enabled("Tests in " + className + " enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ConditionEvaluationResult> checkGlobalParameterSet(ExtensionContext extensionContext, Method testMethod, String testName) {
|
private Optional<ConditionEvaluationResult> checkGlobalParameterSet(ExtensionContext extensionContext, Method testMethod, String testName) {
|
||||||
Store rootStore = extensionContext.getRoot().getStore(Namespace.GLOBAL);
|
Store rootStore = extensionContext.getRoot().getStore(Namespace.GLOBAL);
|
||||||
return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(EnableIfSet.class))
|
return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(EnableIfSet.class))
|
||||||
.map(annotation -> {
|
.map(annotation -> {
|
||||||
if (rootStore == null) {
|
if (rootStore == null) {
|
||||||
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Need to check key '%s' " +
|
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Need to check key '%s' " +
|
||||||
"to be set, but root context is null. Skipping...", testName, annotation.globalKey()));
|
"to be set, but root context is null. Skipping...", testName, annotation.globalKey()));
|
||||||
}
|
}
|
||||||
Object value = rootStore.get(annotation.globalKey());
|
Object value = rootStore.get(annotation.globalKey());
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Key '%s' " +
|
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Key '%s' " +
|
||||||
"is not set in global context. Skipping...", testName, annotation.globalKey()));
|
"is not set in global context. Skipping...", testName, annotation.globalKey()));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).filter(Objects::nonNull)
|
}).filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
|
.orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ConditionEvaluationResult> checkJiraReportingRules(ExtensionContext extensionContext, Method testMethod, String testName) {
|
private Optional<ConditionEvaluationResult> checkJiraReportingRules(ExtensionContext extensionContext, Method testMethod, String testName) {
|
||||||
if (Boolean.parseBoolean(extensionContext.getStore(CONFIG_NAMESPACE).get("reports.tmfj.publish", String.class))) {
|
if (Boolean.parseBoolean(extensionContext.getStore(CONFIG_NAMESPACE).get("reports.tmfj.publish", String.class))) {
|
||||||
return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(JiraTestCase.class))
|
return Optional.of(Arrays.stream(testMethod.getAnnotationsByType(JiraTestCase.class))
|
||||||
.map(annotation -> {
|
.map(annotation -> {
|
||||||
if (JiraTestResultPublisher.doesTestCaseExist(annotation.id(), extensionContext)) {
|
if (JiraTestResultPublisher.doesTestCaseExist(annotation.id(), extensionContext)) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Cannot find Test Case with id '%s' " +
|
return ConditionEvaluationResult.disabled(String.format("Test %s is disabled: Cannot find Test Case with id '%s' " +
|
||||||
"in Jira, but reporting to Jira is requested. Skipping...", testName, annotation.id()));
|
"in Jira, but reporting to Jira is requested. Skipping...", testName, annotation.id()));
|
||||||
}
|
}
|
||||||
}).filter(Objects::nonNull)
|
}).filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
|
.orElse(ConditionEvaluationResult.enabled("Test " + testName + " enabled")));
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ConditionEvaluationResult> checkTestCaseEnvironments(Environment environment, Method testMethod,
|
private Optional<ConditionEvaluationResult> checkTestCaseEnvironments(Environment environment, Method testMethod,
|
||||||
String testName) {
|
String testName) {
|
||||||
return Stream.<Supplier<Optional<Environment[]>>>of(
|
return Stream.<Supplier<Optional<Environment[]>>>of(
|
||||||
() -> Optional.ofNullable(testMethod.getAnnotation(TestCase.class))
|
() -> Optional.ofNullable(testMethod.getAnnotation(TestCase.class))
|
||||||
.map(TestCase::environments),
|
.map(TestCase::environments),
|
||||||
() -> Optional.ofNullable(testMethod.getAnnotation(ParameterizedTestCase.class))
|
() -> Optional.ofNullable(testMethod.getAnnotation(ParameterizedTestCase.class))
|
||||||
.map(ParameterizedTestCase::environments))
|
.map(ParameterizedTestCase::environments))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.filter(envs -> Arrays.stream(envs).noneMatch(environment::equals))
|
.filter(envs -> Arrays.stream(envs).noneMatch(environment::equals))
|
||||||
.map(envs -> ConditionEvaluationResult.disabled("Test " +
|
.map(envs -> ConditionEvaluationResult.disabled("Test " +
|
||||||
testName + " disabled. It is marked to only run on " +
|
testName + " disabled. It is marked to only run on " +
|
||||||
Arrays.stream(envs).map(Enum::name).collect(Collectors.joining(", "))));
|
Arrays.stream(envs).map(Enum::name).collect(Collectors.joining(", "))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<ConditionEvaluationResult> checkDefectEnvironments(Environment environment, Method testMethod,
|
private Optional<ConditionEvaluationResult> checkDefectEnvironments(Environment environment, Method testMethod,
|
||||||
String testName, ExtensionContext context) {
|
String testName, ExtensionContext context) {
|
||||||
return Optional.of(context.getStore(CONFIG_NAMESPACE)
|
return Optional.of(context.getStore(CONFIG_NAMESPACE)
|
||||||
.getOrComputeIfAbsent("harness.defects.ignore", k -> "false"))
|
.getOrComputeIfAbsent("harness.defects.ignore", k -> "false"))
|
||||||
.filter(ignore -> !"true".equals(ignore))
|
.filter(ignore -> !"true".equals(ignore))
|
||||||
.flatMap(i -> Arrays.stream(testMethod.getAnnotationsByType(Defect.class))
|
.flatMap(i -> Arrays.stream(testMethod.getAnnotationsByType(Defect.class))
|
||||||
.filter(d -> Arrays.stream(d.environments()).anyMatch(environment::equals))
|
.filter(d -> Arrays.stream(d.environments()).anyMatch(environment::equals))
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.map(d -> ConditionEvaluationResult.disabled("Test " +
|
.map(d -> ConditionEvaluationResult.disabled("Test " +
|
||||||
testName + " in environment " + environment + " disabled due to defect '" + d.value() + "'"));
|
testName + " in environment " + environment + " disabled due to defect '" + d.value() + "'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Environment getCurrentEnvironment(ExtensionContext context) {
|
private Environment getCurrentEnvironment(ExtensionContext context) {
|
||||||
return Optional.ofNullable(context.getStore(CONFIG_NAMESPACE)
|
return Optional.ofNullable(context.getStore(CONFIG_NAMESPACE)
|
||||||
.get(HarnessConfigConstants.ENVIRONMENT_TYPE))
|
.get(HarnessConfigConstants.ENVIRONMENT_TYPE))
|
||||||
.filter(String.class::isInstance)
|
.filter(String.class::isInstance)
|
||||||
.map(String.class::cast)
|
.map(String.class::cast)
|
||||||
.map(Environment::fromString)
|
.map(Environment::fromString)
|
||||||
.map(e -> {
|
.map(e -> {
|
||||||
logger.info("Detected environment: {}", () -> e.name());
|
logger.info("Detected environment: {}", () -> e.name());
|
||||||
return e;
|
return e;
|
||||||
})
|
})
|
||||||
.orElseThrow(() ->
|
.orElseThrow(() ->
|
||||||
new HarnessConfigurationException("You need to configure mandatory parameter " + HarnessConfigConstants.ENVIRONMENT_TYPE));
|
new HarnessConfigurationException("You need to configure mandatory parameter " + HarnessConfigConstants.ENVIRONMENT_TYPE));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Browser getBrowserFromConfig(ExtensionContext extensionContext) {
|
private Browser getBrowserFromConfig(ExtensionContext extensionContext) {
|
||||||
String browserConfigName = System.getProperty(BROWSER_CONFIG_KEY);
|
String browserConfigName = System.getProperty(BROWSER_CONFIG_KEY);
|
||||||
|
|
||||||
if (browserConfigName == null) {
|
if (browserConfigName == null) {
|
||||||
browserConfigName = Optional.ofNullable(extensionContext.getStore(CONFIG_NAMESPACE).get(BROWSER_CONFIG_KEY, String.class))
|
browserConfigName = Optional.ofNullable(extensionContext.getStore(CONFIG_NAMESPACE).get(BROWSER_CONFIG_KEY, String.class))
|
||||||
.orElseThrow(() -> new HarnessConfigurationException(("You need to configure browser to run tests!")));
|
.orElseThrow(() -> new HarnessConfigurationException(("You need to configure browser to run tests!")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Browser browser = Browser.getBrowserByConfigName(browserConfigName);
|
Browser browser = Browser.getBrowserByConfigName(browserConfigName);
|
||||||
logger.info("Detected browser: {}", browser::name);
|
logger.info("Detected browser: {}", browser::name);
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionEvaluationResult enableEvaluationAndLogExecutionStart(ExtensionContext extensionContext) {
|
private ConditionEvaluationResult enableEvaluationAndLogExecutionStart(ExtensionContext extensionContext) {
|
||||||
String testName = extensionContext.getDisplayName();
|
String testName = extensionContext.getDisplayName();
|
||||||
logger.info("Test case {} execution in test class {} started.", () -> testName, () -> getClassCanonicalName(extensionContext));
|
logger.info("Test case {} execution in test class {} started.", () -> testName, () -> getClassCanonicalName(extensionContext));
|
||||||
return ConditionEvaluationResult.enabled("Test " + testName + " enabled");
|
return ConditionEvaluationResult.enabled("Test " + testName + " enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logTestResult(ExtensionContext extensionContext) {
|
private void logTestResult(ExtensionContext extensionContext) {
|
||||||
Method testMethod = extensionContext.getRequiredTestMethod();
|
Method testMethod = extensionContext.getRequiredTestMethod();
|
||||||
Optional<Throwable> executionException = extensionContext.getExecutionException();
|
Optional<Throwable> executionException = extensionContext.getExecutionException();
|
||||||
String resolution = executionException.map(e -> RESOLUTION_FAIL).orElse(RESOLUTION_PASS);
|
String resolution = executionException.map(e -> RESOLUTION_FAIL).orElse(RESOLUTION_PASS);
|
||||||
|
|
||||||
logger.info("Test case {} in test class {} executed with resolution - {}.", () -> extensionContext.getDisplayName(), () -> testMethod.getDeclaringClass(), () -> resolution);
|
logger.info("Test case {} in test class {} executed with resolution - {}.", () -> extensionContext.getDisplayName(), () -> testMethod.getDeclaringClass(), () -> resolution);
|
||||||
|
|
||||||
if (resolution.equals(RESOLUTION_FAIL)) {
|
if (resolution.equals(RESOLUTION_FAIL)) {
|
||||||
logger.error("ERROR: ", () -> executionException.get());
|
logger.error("ERROR: ", () -> executionException.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getClassCanonicalName(ExtensionContext extensionContext) {
|
private String getClassCanonicalName(ExtensionContext extensionContext) {
|
||||||
return extensionContext.getTestClass()
|
return extensionContext.getTestClass()
|
||||||
.get()
|
.get()
|
||||||
.getCanonicalName();
|
.getCanonicalName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeCurrentTestName(ExtensionContext extensionContext) {
|
private void storeCurrentTestName(ExtensionContext extensionContext) {
|
||||||
String uniqueId = UniqueId.parse(extensionContext.getUniqueId())
|
String uniqueId = UniqueId.parse(extensionContext.getUniqueId())
|
||||||
.getSegments()
|
.getSegments()
|
||||||
.stream()
|
.stream()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(UniqueId.Segment::getValue)
|
.map(UniqueId.Segment::getValue)
|
||||||
.collect(Collectors.joining("."));
|
.collect(Collectors.joining("."));
|
||||||
extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_UNIQUE_ID, uniqueId);
|
extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_UNIQUE_ID, uniqueId);
|
||||||
|
|
||||||
String shortId = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
|
String shortId = (extensionContext.getTestClass().get().getSimpleName() + "_" + extensionContext.getDisplayName()).replaceAll("[\\\\/:*?\"<>|]", "");
|
||||||
extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_SHORT_ID, shortId);
|
extensionContext.getRoot().getStore(Namespace.GLOBAL).put(HarnessConfigConstants.TEST_SHORT_ID, shortId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,33 +1,33 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Repeatable;
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.annotations.Environment.DEV;
|
import static cz.moneta.test.harness.annotations.Environment.DEV;
|
||||||
import static cz.moneta.test.harness.annotations.Environment.EDU;
|
import static cz.moneta.test.harness.annotations.Environment.EDU;
|
||||||
import static cz.moneta.test.harness.annotations.Environment.FVE;
|
import static cz.moneta.test.harness.annotations.Environment.FVE;
|
||||||
import static cz.moneta.test.harness.annotations.Environment.PPE;
|
import static cz.moneta.test.harness.annotations.Environment.PPE;
|
||||||
import static cz.moneta.test.harness.annotations.Environment.TST1;
|
import static cz.moneta.test.harness.annotations.Environment.TST1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables test in specified environments with a reference to the related issue
|
* Disables test in specified environments with a reference to the related issue
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Repeatable(Defects.class)
|
@Repeatable(Defects.class)
|
||||||
public @interface Defect {
|
public @interface Defect {
|
||||||
/**
|
/**
|
||||||
* Reason for test being disabled (ideally) including a reference to a JIRA or TEAMTRACK issue
|
* Reason for test being disabled (ideally) including a reference to a JIRA or TEAMTRACK issue
|
||||||
*/
|
*/
|
||||||
String value();
|
String value();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicable environments where the test should be effectively disabled (default is all environments)
|
* Applicable environments where the test should be effectively disabled (default is all environments)
|
||||||
*/
|
*/
|
||||||
Environment[] environments() default {DEV, TST1, PPE, EDU, FVE};
|
Environment[] environments() default {DEV, TST1, PPE, EDU, FVE};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* container annotation for {@link cz.moneta.test.harness.annotations.Defect}
|
* container annotation for {@link cz.moneta.test.harness.annotations.Defect}
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface Defects {
|
public @interface Defects {
|
||||||
Defect[] value();
|
Defect[] value();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Repeatable(EnableIfSets.class)
|
@Repeatable(EnableIfSets.class)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface EnableIfSet {
|
public @interface EnableIfSet {
|
||||||
|
|
||||||
String globalKey();
|
String globalKey();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,15 +1,15 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container annotation
|
* Container annotation
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface EnableIfSets {
|
public @interface EnableIfSets {
|
||||||
|
|
||||||
EnableIfSet[] value();
|
EnableIfSet[] value();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,30 +1,30 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
public enum Environment {
|
public enum Environment {
|
||||||
DEV("DigDev"),
|
DEV("DigDev"),
|
||||||
TST1("TST1"),
|
TST1("TST1"),
|
||||||
TST3("TST3"),
|
TST3("TST3"),
|
||||||
PPE("PPE"),
|
PPE("PPE"),
|
||||||
EDU("EDU"),
|
EDU("EDU"),
|
||||||
FVE("FVE"),
|
FVE("FVE"),
|
||||||
LIVE("LIVE");
|
LIVE("LIVE");
|
||||||
|
|
||||||
Environment(String jiraAlias) {
|
Environment(String jiraAlias) {
|
||||||
this.jiraAlias = jiraAlias;
|
this.jiraAlias = jiraAlias;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String jiraAlias;
|
private String jiraAlias;
|
||||||
|
|
||||||
public static Environment fromString(String env) {
|
public static Environment fromString(String env) {
|
||||||
return EnumSet.allOf(Environment.class).stream()
|
return EnumSet.allOf(Environment.class).stream()
|
||||||
.filter(e -> e.name().equalsIgnoreCase(env))
|
.filter(e -> e.name().equalsIgnoreCase(env))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJiraAlias() {
|
public String getJiraAlias() {
|
||||||
return jiraAlias;
|
return jiraAlias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,22 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code @JiraTestCase} is used to signal that the annotated method implements
|
* {@code @JiraTestCase} is used to signal that the annotated method implements
|
||||||
* particular test case in Test Management for JIRA.
|
* particular test case in Test Management for JIRA.
|
||||||
*
|
*
|
||||||
* The info in this annotation is used by harness to create Test Run objects in
|
* The info in this annotation is used by harness to create Test Run objects in
|
||||||
* Test Management for JIRA when configured and enabled, otherwise it has no effect.
|
* Test Management for JIRA when configured and enabled, otherwise it has no effect.
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Repeatable(JiraTestCases.class)
|
@Repeatable(JiraTestCases.class)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface JiraTestCase {
|
public @interface JiraTestCase {
|
||||||
|
|
||||||
String id();
|
String id();
|
||||||
|
|
||||||
String project() default "";
|
String project() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container annotation
|
* Container annotation
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface JiraTestCases {
|
public @interface JiraTestCases {
|
||||||
|
|
||||||
JiraTestCase[] value();
|
JiraTestCase[] value();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +1,31 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import cz.moneta.test.harness.HarnessJunit5Extension;
|
import cz.moneta.test.harness.HarnessJunit5Extension;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.platform.runner.JUnitPlatform;
|
import org.junit.platform.runner.JUnitPlatform;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This annotation is used for managing parallel test execution.
|
* This annotation is used for managing parallel test execution.
|
||||||
* Test class with this annotation will not be executed in parallel to other classes with this annotation.
|
* Test class with this annotation will not be executed in parallel to other classes with this annotation.
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@RunWith(JUnitPlatform.class)
|
@RunWith(JUnitPlatform.class)
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@ExtendWith(HarnessJunit5Extension.class)
|
@ExtendWith(HarnessJunit5Extension.class)
|
||||||
public @interface NonConcurrentTestScenario {
|
public @interface NonConcurrentTestScenario {
|
||||||
|
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
public @interface ParameterizedTestCase {
|
public @interface ParameterizedTestCase {
|
||||||
|
|
||||||
String name () default "";
|
String name () default "";
|
||||||
|
|
||||||
Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
|
Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
|
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Test
|
@Test
|
||||||
public @interface TestCase {
|
public @interface TestCase {
|
||||||
|
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
|
Environment[] environments() default {Environment.DEV, Environment.TST1, Environment.PPE, Environment.EDU, Environment.FVE};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
public @interface TestContext {
|
public @interface TestContext {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,26 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import cz.moneta.test.harness.HarnessJunit5Extension;
|
import cz.moneta.test.harness.HarnessJunit5Extension;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.parallel.Execution;
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
import org.junit.jupiter.api.parallel.ExecutionMode;
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
import org.junit.platform.runner.JUnitPlatform;
|
import org.junit.platform.runner.JUnitPlatform;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@RunWith(JUnitPlatform.class)
|
@RunWith(JUnitPlatform.class)
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(ExecutionMode.CONCURRENT)
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
@ExtendWith(HarnessJunit5Extension.class)
|
@ExtendWith(HarnessJunit5Extension.class)
|
||||||
public @interface TestScenario {
|
public @interface TestScenario {
|
||||||
|
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
||||||
}
|
}
|
||||||
@ -1,29 +1,29 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import cz.moneta.test.harness.HarnessJunit5Extension;
|
import cz.moneta.test.harness.HarnessJunit5Extension;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.api.parallel.Execution;
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
import org.junit.jupiter.api.parallel.ExecutionMode;
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
import org.junit.platform.runner.JUnitPlatform;
|
import org.junit.platform.runner.JUnitPlatform;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@RunWith(JUnitPlatform.class)
|
@RunWith(JUnitPlatform.class)
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@Execution(ExecutionMode.SAME_THREAD)
|
@Execution(ExecutionMode.SAME_THREAD)
|
||||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
@ExtendWith(HarnessJunit5Extension.class)
|
@ExtendWith(HarnessJunit5Extension.class)
|
||||||
public @interface TestScenarioWithOrder {
|
public @interface TestScenarioWithOrder {
|
||||||
|
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
Browser[] browsers() default {Browser.MS_EDGE, Browser.GOOGLE_CHROME};
|
||||||
}
|
}
|
||||||
@ -1,19 +1,19 @@
|
|||||||
package cz.moneta.test.harness.annotations;
|
package cz.moneta.test.harness.annotations;
|
||||||
|
|
||||||
import cz.moneta.test.harness.HarnessJunit5Extension;
|
import cz.moneta.test.harness.HarnessJunit5Extension;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.platform.runner.JUnitPlatform;
|
import org.junit.platform.runner.JUnitPlatform;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@RunWith(JUnitPlatform.class)
|
@RunWith(JUnitPlatform.class)
|
||||||
@ExtendWith(HarnessJunit5Extension.class)
|
@ExtendWith(HarnessJunit5Extension.class)
|
||||||
public @interface TestSuite {
|
public @interface TestSuite {
|
||||||
|
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,117 +1,117 @@
|
|||||||
package cz.moneta.test.harness.config;
|
package cz.moneta.test.harness.config;
|
||||||
|
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public final class ConfigProvider {
|
public final class ConfigProvider {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(ConfigProvider.class);
|
private static final Logger logger = LogManager.getLogger(ConfigProvider.class);
|
||||||
|
|
||||||
private ConfigProvider() {}
|
private ConfigProvider() {}
|
||||||
|
|
||||||
public static Map<String, String> readConfig() {
|
public static Map<String, String> readConfig() {
|
||||||
String fileName = Stream.<Supplier<String>>of(
|
String fileName = Stream.<Supplier<String>>of(
|
||||||
() -> System.getProperty("config"),
|
() -> System.getProperty("config"),
|
||||||
() -> System.getenv("HARNESS_CONFIG"))
|
() -> System.getenv("HARNESS_CONFIG"))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new HarnessConfigurationException("You need to provide a configuration file"));
|
.orElseThrow(() -> new HarnessConfigurationException("You need to provide a configuration file"));
|
||||||
|
|
||||||
Map<String, String> resultConfig = loadConfig(fileName, new ArrayList<>());
|
Map<String, String> resultConfig = loadConfig(fileName, new ArrayList<>());
|
||||||
logger.debug("Effective configuration:");
|
logger.debug("Effective configuration:");
|
||||||
resultConfig.entrySet()
|
resultConfig.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(configEntry -> !configEntry.getKey().equals(HarnessConfigConstants.VAULT_PASSWORD_CONFIG)) //DO NOT LOG VAULT PWD VALUE!
|
.filter(configEntry -> !configEntry.getKey().equals(HarnessConfigConstants.VAULT_PASSWORD_CONFIG)) //DO NOT LOG VAULT PWD VALUE!
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, configEntry -> configEntry.getValue().trim()))
|
.collect(Collectors.toMap(Map.Entry::getKey, configEntry -> configEntry.getValue().trim()))
|
||||||
.forEach((key, value) -> logger.debug("{}={}", key, value));
|
.forEach((key, value) -> logger.debug("{}={}", key, value));
|
||||||
return resultConfig;
|
return resultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, String> loadConfig(String fileName, List<String> configStack) {
|
private static Map<String, String> loadConfig(String fileName, List<String> configStack) {
|
||||||
Map<String, String> properties = readProperties(fileName);
|
Map<String, String> properties = readProperties(fileName);
|
||||||
|
|
||||||
return Optional.ofNullable(properties.get("environment.from"))
|
return Optional.ofNullable(properties.get("environment.from"))
|
||||||
.map(fn -> {
|
.map(fn -> {
|
||||||
if (configStack.contains(fn)) {
|
if (configStack.contains(fn)) {
|
||||||
throw new IllegalStateException("Config chain contains circular dependencies");
|
throw new IllegalStateException("Config chain contains circular dependencies");
|
||||||
}
|
}
|
||||||
return fn;
|
return fn;
|
||||||
})
|
})
|
||||||
.map(fn -> {
|
.map(fn -> {
|
||||||
configStack.add(fn);
|
configStack.add(fn);
|
||||||
return loadConfig(fn, configStack);
|
return loadConfig(fn, configStack);
|
||||||
})
|
})
|
||||||
.map(p -> {
|
.map(p -> {
|
||||||
p.putAll(properties);
|
p.putAll(properties);
|
||||||
return p;
|
return p;
|
||||||
})
|
})
|
||||||
.orElse(properties);
|
.orElse(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, String> readProperties(String fileName) {
|
private static Map<String, String> readProperties(String fileName) {
|
||||||
Reader reader;
|
Reader reader;
|
||||||
try {
|
try {
|
||||||
logger.debug(() -> String.format("Trying to load config file: %s", fileName));
|
logger.debug(() -> String.format("Trying to load config file: %s", fileName));
|
||||||
reader = new FileReader(fileName);
|
reader = new FileReader(fileName);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
logger.debug(() ->
|
logger.debug(() ->
|
||||||
String.format("Could not load file: %s, trying to load it as classpath resource", fileName));
|
String.format("Could not load file: %s, trying to load it as classpath resource", fileName));
|
||||||
reader = getReaderForClasspathResource(fileName);
|
reader = getReaderForClasspathResource(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
try {
|
try {
|
||||||
properties.load(reader);
|
properties.load(reader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Cannot read config file", e);
|
throw new IllegalStateException("Cannot read config file", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return properties.stringPropertyNames().stream()
|
return properties.stringPropertyNames().stream()
|
||||||
.collect(Collectors.toMap(k -> k, k -> resolveSystemProperty(properties.getProperty(k))));
|
.collect(Collectors.toMap(k -> k, k -> resolveSystemProperty(properties.getProperty(k))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String resolveSystemProperty(String value) {
|
private static String resolveSystemProperty(String value) {
|
||||||
return Stream.<Supplier<Optional<String>>>of(
|
return Stream.<Supplier<Optional<String>>>of(
|
||||||
() -> Optional.ofNullable(value).filter(v -> !value.startsWith("$")),
|
() -> Optional.ofNullable(value).filter(v -> !value.startsWith("$")),
|
||||||
() -> Optional.ofNullable(value)
|
() -> Optional.ofNullable(value)
|
||||||
.map(v -> System.getProperty(v.substring(1)))
|
.map(v -> System.getProperty(v.substring(1)))
|
||||||
.map(v -> {
|
.map(v -> {
|
||||||
logger.debug("Successfully resolved system property: {}", () -> value);
|
logger.debug("Successfully resolved system property: {}", () -> value);
|
||||||
return v;
|
return v;
|
||||||
}))
|
}))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("Cannot find system property for: " + value));
|
.orElseThrow(() -> new IllegalStateException("Cannot find system property for: " + value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Reader getReaderForClasspathResource(String resourceName) {
|
private static Reader getReaderForClasspathResource(String resourceName) {
|
||||||
String resourcePath = "envs/" + resourceName; //TODO hidden magic constant - fix this concept
|
String resourcePath = "envs/" + resourceName; //TODO hidden magic constant - fix this concept
|
||||||
InputStream resourceAsStream = ConfigProvider.class.getClassLoader().getResourceAsStream(resourcePath);
|
InputStream resourceAsStream = ConfigProvider.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||||
if (resourceAsStream != null) {
|
if (resourceAsStream != null) {
|
||||||
return new InputStreamReader(resourceAsStream);
|
return new InputStreamReader(resourceAsStream);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(String.format("Cannot find config file: %s", resourceName));
|
throw new IllegalStateException(String.format("Cannot find config file: %s", resourceName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
public interface Connector {
|
public interface Connector {
|
||||||
|
|
||||||
default void close() {
|
default void close() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,39 +1,39 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class DemoConnector implements Connector {
|
public class DemoConnector implements Connector {
|
||||||
|
|
||||||
private Path directory;
|
private Path directory;
|
||||||
|
|
||||||
public void connectToTempDirectoryService(String name) {
|
public void connectToTempDirectoryService(String name) {
|
||||||
try {
|
try {
|
||||||
directory = Files.createTempDirectory(name);
|
directory = Files.createTempDirectory(name);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Cannot connect to temp filesystem service", e);
|
throw new RuntimeException("Cannot connect to temp filesystem service", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createFile(String name) {
|
public void createFile(String name) {
|
||||||
try {
|
try {
|
||||||
Files.createFile(directory.resolve(name));
|
Files.createFile(directory.resolve(name));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(String.format("Cannot create file: %s", name), e);
|
throw new RuntimeException(String.format("Cannot create file: %s", name), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean fileExists(String name) {
|
public boolean fileExists(String name) {
|
||||||
return Files.exists(directory.resolve(name));
|
return Files.exists(directory.resolve(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteFile(String name) {
|
public void deleteFile(String name) {
|
||||||
try {
|
try {
|
||||||
Files.delete(directory.resolve(name));
|
Files.delete(directory.resolve(name));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(String.format("Cannot delete file: %s", name), e);
|
throw new RuntimeException(String.format("Cannot delete file: %s", name), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,156 +1,156 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
import com.bytezone.dm3270.ConnectionListener;
|
import com.bytezone.dm3270.ConnectionListener;
|
||||||
import com.bytezone.dm3270.TerminalClient;
|
import com.bytezone.dm3270.TerminalClient;
|
||||||
import com.bytezone.dm3270.commands.AIDCommand;
|
import com.bytezone.dm3270.commands.AIDCommand;
|
||||||
import com.bytezone.dm3270.display.ScreenDimensions;
|
import com.bytezone.dm3270.display.ScreenDimensions;
|
||||||
import cz.moneta.test.harness.support.greenscreen.Key;
|
import cz.moneta.test.harness.support.greenscreen.Key;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.glassfish.jersey.SslConfigurator;
|
import org.glassfish.jersey.SslConfigurator;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class GreenScreenConnector implements Connector {
|
public class GreenScreenConnector implements Connector {
|
||||||
|
|
||||||
public static final String CLIENT = "08548";
|
public static final String CLIENT = "08548";
|
||||||
private static final Logger LOG = LogManager.getLogger("GreenScreenConnector");
|
private static final Logger LOG = LogManager.getLogger("GreenScreenConnector");
|
||||||
private final Semaphore lock = new Semaphore(0);
|
private final Semaphore lock = new Semaphore(0);
|
||||||
private final TerminalClient terminal;
|
private final TerminalClient terminal;
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String user;
|
private final String user;
|
||||||
private final String password;
|
private final String password;
|
||||||
|
|
||||||
public GreenScreenConnector(String url, String user, String password) {
|
public GreenScreenConnector(String url, String user, String password) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
|
|
||||||
this.terminal = new TerminalClient(0, new ScreenDimensions(24, 80));
|
this.terminal = new TerminalClient(0, new ScreenDimensions(24, 80));
|
||||||
terminal.setConnectionTimeoutMillis(20_000);
|
terminal.setConnectionTimeoutMillis(20_000);
|
||||||
terminal.setConnectionListener(buildConnectionListener());
|
terminal.setConnectionListener(buildConnectionListener());
|
||||||
terminal.addScreenChangeListener(sw -> {
|
terminal.addScreenChangeListener(sw -> {
|
||||||
LOG.debug(() -> "screen changed: \n" + toCp870(terminal.getScreenText()));
|
LOG.debug(() -> "screen changed: \n" + toCp870(terminal.getScreenText()));
|
||||||
lock.release();
|
lock.release();
|
||||||
});
|
});
|
||||||
terminal.setSocketFactory(buildSocketFactory());
|
terminal.setSocketFactory(buildSocketFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectionListener buildConnectionListener() {
|
private static ConnectionListener buildConnectionListener() {
|
||||||
return new ConnectionListener() {
|
return new ConnectionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnection() {
|
public void onConnection() {
|
||||||
LOG.info("CONNECTED!!!");
|
LOG.info("CONNECTED!!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onException(Exception e) {
|
public void onException(Exception e) {
|
||||||
LOG.error("Green screen connection failed", e);
|
LOG.error("Green screen connection failed", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionClosed() {
|
public void onConnectionClosed() {
|
||||||
LOG.info("CONNECTION CLOSED!!!");
|
LOG.info("CONNECTION CLOSED!!!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toCp870(String cp1047) {
|
private static String toCp870(String cp1047) {
|
||||||
try {
|
try {
|
||||||
return new String(cp1047.getBytes("CP1047"), "CP870");
|
return new String(cp1047.getBytes("CP1047"), "CP870");
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
throw new IllegalStateException("Error converting CP1047 to CP870");
|
throw new IllegalStateException("Error converting CP1047 to CP870");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toCp1047(String cp870) {
|
private static String toCp1047(String cp870) {
|
||||||
try {
|
try {
|
||||||
return new String(cp870.getBytes("CP870"), "CP1047");
|
return new String(cp870.getBytes("CP870"), "CP1047");
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
throw new IllegalStateException("Error converting CP870 to CP1047");
|
throw new IllegalStateException("Error converting CP870 to CP1047");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectAndLogin() {
|
public void connectAndLogin() {
|
||||||
terminal.connect(url, 992);
|
terminal.connect(url, 992);
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
terminal.setFieldTextByCoord(24, 1, "atrf");
|
terminal.setFieldTextByCoord(24, 1, "atrf");
|
||||||
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
terminal.setFieldTextByCoord(6, 25, CLIENT);
|
terminal.setFieldTextByCoord(6, 25, CLIENT);
|
||||||
terminal.setFieldTextByCoord(8, 24, user);
|
terminal.setFieldTextByCoord(8, 24, user);
|
||||||
terminal.setFieldTextByCoord(10, 24, password);
|
terminal.setFieldTextByCoord(10, 24, password);
|
||||||
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForScreenChange() {
|
private void waitForScreenChange() {
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
try {
|
try {
|
||||||
success = lock.tryAcquire(5L, TimeUnit.SECONDS);
|
success = lock.tryAcquire(5L, TimeUnit.SECONDS);
|
||||||
TimeUnit.MILLISECONDS.sleep(200);
|
TimeUnit.MILLISECONDS.sleep(200);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
LOG.error(e);
|
LOG.error(e);
|
||||||
}
|
}
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new IllegalStateException("Failed to load screen within 5 second timeout");
|
throw new IllegalStateException("Failed to load screen within 5 second timeout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SSLSocketFactory buildSocketFactory() {
|
private SSLSocketFactory buildSocketFactory() {
|
||||||
try (InputStream truststoreIs = this.getClass().getClassLoader()
|
try (InputStream truststoreIs = this.getClass().getClassLoader()
|
||||||
.getResourceAsStream("keystores/greenScreenTrustStore")) {
|
.getResourceAsStream("keystores/greenScreenTrustStore")) {
|
||||||
|
|
||||||
KeyStore trustedStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore trustedStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
trustedStore.load(truststoreIs, "changeit".toCharArray());
|
trustedStore.load(truststoreIs, "changeit".toCharArray());
|
||||||
|
|
||||||
return SslConfigurator.newInstance()
|
return SslConfigurator.newInstance()
|
||||||
.trustStore(trustedStore)
|
.trustStore(trustedStore)
|
||||||
.keyStorePassword("changeit".toCharArray())
|
.keyStorePassword("changeit".toCharArray())
|
||||||
.createSSLContext()
|
.createSSLContext()
|
||||||
.getSocketFactory();
|
.getSocketFactory();
|
||||||
|
|
||||||
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException e) {
|
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException e) {
|
||||||
throw new IllegalStateException("Failed to initialize SSL context", e);
|
throw new IllegalStateException("Failed to initialize SSL context", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(int row, int column, int length) {
|
public String getText(int row, int column, int length) {
|
||||||
String screenText = toCp870(terminal.getScreenText());
|
String screenText = toCp870(terminal.getScreenText());
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return screenText;
|
return screenText;
|
||||||
} else {
|
} else {
|
||||||
int beginIndex = (row - 1) * 81 + column - 1;
|
int beginIndex = (row - 1) * 81 + column - 1;
|
||||||
return screenText.substring(beginIndex, beginIndex + length);
|
return screenText.substring(beginIndex, beginIndex + length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pressKey(Key key) {
|
public void pressKey(Key key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ENTER:
|
case ENTER:
|
||||||
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
terminal.sendAID(AIDCommand.AID_ENTER, "ENTER");
|
||||||
waitForScreenChange();
|
waitForScreenChange();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown key " + key.name());
|
throw new IllegalArgumentException("Unknown key " + key.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(int row, int column, String text) {
|
public void type(int row, int column, String text) {
|
||||||
terminal.setFieldTextByCoord(row, column, toCp1047(text));
|
terminal.setFieldTextByCoord(row, column, toCp1047(text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,129 +1,129 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerEnvironmentList;
|
import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerEnvironmentList;
|
||||||
import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerSourceSystem;
|
import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerSourceSystem;
|
||||||
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToClient;
|
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToClient;
|
||||||
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToServer;
|
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToServer;
|
||||||
import itSodTesting.sharedResources.utils.getUser.LoggedUser;
|
import itSodTesting.sharedResources.utils.getUser.LoggedUser;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
|
|
||||||
public class IlodsServerConnector implements Connector {
|
public class IlodsServerConnector implements Connector {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(IlodsServerConnector.class);
|
private static final Logger logger = LogManager.getLogger(IlodsServerConnector.class);
|
||||||
|
|
||||||
private static final String token = "&PE$T.Q;1SXdhF@#$vdnv3,M8r5+dr=e";
|
private static final String token = "&PE$T.Q;1SXdhF@#$vdnv3,M8r5+dr=e";
|
||||||
|
|
||||||
private static final Pair<String, String> liveServer = Pair.of("primary", "MBCZVW1DL0LRC02.mbid.cz");
|
private static final Pair<String, String> liveServer = Pair.of("primary", "MBCZVW1DL0LRC02.mbid.cz");
|
||||||
private static final Pair<String, String> backupServer = Pair.of("secondary", "MBCZVW0BL0LRC01.mbid.cz");
|
private static final Pair<String, String> backupServer = Pair.of("secondary", "MBCZVW0BL0LRC01.mbid.cz");
|
||||||
private static final Pair<String, String> devServer = Pair.of("develop", "MBCZDWHQXX10366.mbid.cz");
|
private static final Pair<String, String> devServer = Pair.of("develop", "MBCZDWHQXX10366.mbid.cz");
|
||||||
|
|
||||||
private Pair<String, String> currentServer = liveServer;
|
private Pair<String, String> currentServer = liveServer;
|
||||||
// private Pair<String, String> currentServer = devServer;
|
// private Pair<String, String> currentServer = devServer;
|
||||||
|
|
||||||
private IlodsServerEnvironmentList environment;
|
private IlodsServerEnvironmentList environment;
|
||||||
|
|
||||||
public IlodsServerConnector(IlodsServerEnvironmentList environment) {
|
public IlodsServerConnector(IlodsServerEnvironmentList environment) {
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectTransferToClient sendRequest(ObjectTransferToServer data) {
|
public ObjectTransferToClient sendRequest(ObjectTransferToServer data) {
|
||||||
ObjectTransferToClient receivedObject = connectAndReceiveData(data);
|
ObjectTransferToClient receivedObject = connectAndReceiveData(data);
|
||||||
|
|
||||||
// no response = second try
|
// no response = second try
|
||||||
if (receivedObject == null) {
|
if (receivedObject == null) {
|
||||||
switchServer();
|
switchServer();
|
||||||
receivedObject = connectAndReceiveData(data);
|
receivedObject = connectAndReceiveData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receivedObject == null) {
|
if (receivedObject == null) {
|
||||||
throw new RuntimeException("Cannot connect to Ilods servers");
|
throw new RuntimeException("Cannot connect to Ilods servers");
|
||||||
}
|
}
|
||||||
|
|
||||||
return receivedObject;
|
return receivedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectTransferToClient connectAndReceiveData(ObjectTransferToServer data) {
|
private ObjectTransferToClient connectAndReceiveData(ObjectTransferToServer data) {
|
||||||
ObjectTransferToClient receivedObject = null;
|
ObjectTransferToClient receivedObject = null;
|
||||||
Socket clientSocket = null;
|
Socket clientSocket = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clientSocket = new Socket();
|
clientSocket = new Socket();
|
||||||
clientSocket.connect(new InetSocketAddress(currentServer.getRight(), 51001), 1000);
|
clientSocket.connect(new InetSocketAddress(currentServer.getRight(), 51001), 1000);
|
||||||
clientSocket.setSoTimeout(60 * 1000);
|
clientSocket.setSoTimeout(60 * 1000);
|
||||||
|
|
||||||
ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
|
ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
|
||||||
ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());
|
ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());
|
||||||
|
|
||||||
// add clients informations do data
|
// add clients informations do data
|
||||||
data.setToken(token);
|
data.setToken(token);
|
||||||
data.setSourceSystem(IlodsServerSourceSystem.HARNESS);
|
data.setSourceSystem(IlodsServerSourceSystem.HARNESS);
|
||||||
data.setMachineId(LoggedUser.getComputerName());
|
data.setMachineId(LoggedUser.getComputerName());
|
||||||
data.setClientId(LoggedUser.getUserName());
|
data.setClientId(LoggedUser.getUserName());
|
||||||
data.setEnvironment(environment);
|
data.setEnvironment(environment);
|
||||||
|
|
||||||
// pack and sending object data to server
|
// pack and sending object data to server
|
||||||
outToServer.writeObject(data);
|
outToServer.writeObject(data);
|
||||||
|
|
||||||
// unpack and reading object response from server
|
// unpack and reading object response from server
|
||||||
Object transferedObject = inFromServer.readObject();
|
Object transferedObject = inFromServer.readObject();
|
||||||
receivedObject = (ObjectTransferToClient) transferedObject;
|
receivedObject = (ObjectTransferToClient) transferedObject;
|
||||||
|
|
||||||
// recognize and process errors
|
// recognize and process errors
|
||||||
if (receivedObject.getResultCode() == 12) {
|
if (receivedObject.getResultCode() == 12) {
|
||||||
throw new RuntimeException("Fatal error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
|
throw new RuntimeException("Fatal error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
|
||||||
} else if (receivedObject.getResultCode() != 0) {
|
} else if (receivedObject.getResultCode() != 0) {
|
||||||
throw new RuntimeException("Server error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
|
throw new RuntimeException("Server error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
logger.warn("Connection timed out - " + currentServer.getLeft() + " Ilods server didn't respond within the specified interval");
|
logger.warn("Connection timed out - " + currentServer.getLeft() + " Ilods server didn't respond within the specified interval");
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
} catch (ClassNotFoundException | IOException e) {
|
||||||
logger.warn("Ilods server (" + currentServer.getLeft() + ") unavailable", e);
|
logger.warn("Ilods server (" + currentServer.getLeft() + ") unavailable", e);
|
||||||
} finally {
|
} finally {
|
||||||
closeConnection(clientSocket);
|
closeConnection(clientSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
return receivedObject;
|
return receivedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchServer() {
|
private void switchServer() {
|
||||||
// Set up switch between primary/secondary Ilods Server (with check develop mode)
|
// Set up switch between primary/secondary Ilods Server (with check develop mode)
|
||||||
if (currentServer == devServer) {
|
if (currentServer == devServer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentServer == liveServer) {
|
if (currentServer == liveServer) {
|
||||||
currentServer = backupServer;
|
currentServer = backupServer;
|
||||||
} else {
|
} else {
|
||||||
currentServer = liveServer;
|
currentServer = liveServer;
|
||||||
}
|
}
|
||||||
logger.info("Trying to call " + currentServer.getLeft() + " Ilods server");
|
logger.info("Trying to call " + currentServer.getLeft() + " Ilods server");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeConnection(Socket socket) {
|
private void closeConnection(Socket socket) {
|
||||||
if (socket == null)
|
if (socket == null)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
socket.shutdownInput();
|
socket.shutdownInput();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
socket.shutdownOutput();
|
socket.shutdownOutput();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,123 +1,123 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
import com.bettercloud.vault.SslConfig;
|
import com.bettercloud.vault.SslConfig;
|
||||||
import com.bettercloud.vault.Vault;
|
import com.bettercloud.vault.Vault;
|
||||||
import com.bettercloud.vault.VaultConfig;
|
import com.bettercloud.vault.VaultConfig;
|
||||||
import com.bettercloud.vault.VaultException;
|
import com.bettercloud.vault.VaultException;
|
||||||
import com.bettercloud.vault.api.Logical;
|
import com.bettercloud.vault.api.Logical;
|
||||||
import com.bettercloud.vault.response.LogicalResponse;
|
import com.bettercloud.vault.response.LogicalResponse;
|
||||||
import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
|
import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
|
||||||
import cz.moneta.test.harness.support.auth.Credentials;
|
import cz.moneta.test.harness.support.auth.Credentials;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class VaultConnector extends BaseRestConnector {
|
public class VaultConnector extends BaseRestConnector {
|
||||||
|
|
||||||
private static final Logger LOG = LogManager.getLogger(VaultConnector.class);
|
private static final Logger LOG = LogManager.getLogger(VaultConnector.class);
|
||||||
|
|
||||||
private static String url;
|
private static String url;
|
||||||
private static String username;
|
private static String username;
|
||||||
private static String pwd;
|
private static String pwd;
|
||||||
|
|
||||||
private static Vault vault;
|
private static Vault vault;
|
||||||
private static VaultConfig config;
|
private static VaultConfig config;
|
||||||
|
|
||||||
private static LocalDateTime tokenExpiration;
|
private static LocalDateTime tokenExpiration;
|
||||||
|
|
||||||
public VaultConnector(String url, String username, String pwd) {
|
public VaultConnector(String url, String username, String pwd) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.pwd = pwd;
|
this.pwd = pwd;
|
||||||
|
|
||||||
if (tokenExpiration == null) {
|
if (tokenExpiration == null) {
|
||||||
initVaultConnection();
|
initVaultConnection();
|
||||||
} else if (tokenExpiration.minusMinutes(1).isBefore(LocalDateTime.now())) {
|
} else if (tokenExpiration.minusMinutes(1).isBefore(LocalDateTime.now())) {
|
||||||
loginVault();
|
loginVault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initVaultConnection() {
|
private void initVaultConnection() {
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
keyStore.load(this.getClass().getClassLoader().getResourceAsStream("keystores/mb_root"), "changeit".toCharArray());
|
keyStore.load(this.getClass().getClassLoader().getResourceAsStream("keystores/mb_root"), "changeit".toCharArray());
|
||||||
config = new VaultConfig()
|
config = new VaultConfig()
|
||||||
.address(url)
|
.address(url)
|
||||||
.sslConfig(new SslConfig()
|
.sslConfig(new SslConfig()
|
||||||
.trustStore(keyStore)
|
.trustStore(keyStore)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
vault = new Vault(config, 2);
|
vault = new Vault(config, 2);
|
||||||
|
|
||||||
loginVault();
|
loginVault();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException("Failed to connect to vault url: " + url, e);
|
throw new IllegalStateException("Failed to connect to vault url: " + url, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loginVault() {
|
private void loginVault() {
|
||||||
try {
|
try {
|
||||||
String token = vault.auth().loginByUserPass(username, pwd).getAuthClientToken();
|
String token = vault.auth().loginByUserPass(username, pwd).getAuthClientToken();
|
||||||
config.token(token).build();
|
config.token(token).build();
|
||||||
|
|
||||||
long ttl = vault.auth().lookupSelf().getTTL();
|
long ttl = vault.auth().lookupSelf().getTTL();
|
||||||
tokenExpiration = LocalDateTime.now().plusSeconds(ttl);
|
tokenExpiration = LocalDateTime.now().plusSeconds(ttl);
|
||||||
} catch (VaultException e) {
|
} catch (VaultException e) {
|
||||||
LOG.error("Cannot get Vault token", e);
|
LOG.error("Cannot get Vault token", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Credentials> getUsernameAndPassword(String path) {
|
public Optional<Credentials> getUsernameAndPassword(String path) {
|
||||||
return Optional.ofNullable(vault.logical())
|
return Optional.ofNullable(vault.logical())
|
||||||
.map(v -> readValue(path, v))
|
.map(v -> readValue(path, v))
|
||||||
.map(LogicalResponse::getDataObject)
|
.map(LogicalResponse::getDataObject)
|
||||||
.flatMap(d -> Optional.ofNullable(d.getString("username")).map(usr -> Pair.of(usr, d)))
|
.flatMap(d -> Optional.ofNullable(d.getString("username")).map(usr -> Pair.of(usr, d)))
|
||||||
.flatMap(p -> Optional.ofNullable(p.getRight().getString("password")).map(pwd -> new Credentials(p.getLeft(), pwd)));
|
.flatMap(p -> Optional.ofNullable(p.getRight().getString("password")).map(pwd -> new Credentials(p.getLeft(), pwd)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> Optional<T> getValue(String path, String key) {
|
public <T> Optional<T> getValue(String path, String key) {
|
||||||
return Optional.ofNullable(vault.logical())
|
return Optional.ofNullable(vault.logical())
|
||||||
.map(v -> readValue(path, v))
|
.map(v -> readValue(path, v))
|
||||||
.map(LogicalResponse::getDataObject)
|
.map(LogicalResponse::getDataObject)
|
||||||
.map(d -> (T) d.getString(key));
|
.map(d -> (T) d.getString(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyValue(String path, String key, String value) {
|
public void setKeyValue(String path, String key, String value) {
|
||||||
Optional.ofNullable(readValue(path, vault.logical()))
|
Optional.ofNullable(readValue(path, vault.logical()))
|
||||||
.map(LogicalResponse::getData)
|
.map(LogicalResponse::getData)
|
||||||
.map(map -> new HashMap<String, Object>(map))
|
.map(map -> new HashMap<String, Object>(map))
|
||||||
.map(m -> {
|
.map(m -> {
|
||||||
m.put(key, value);
|
m.put(key, value);
|
||||||
return m;
|
return m;
|
||||||
})
|
})
|
||||||
.map(m -> writeValue(path, m, vault.logical()))
|
.map(m -> writeValue(path, m, vault.logical()))
|
||||||
.orElseThrow(() -> new IllegalStateException("Cannot write value to vault"));
|
.orElseThrow(() -> new IllegalStateException("Cannot write value to vault"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LogicalResponse writeValue(String path, Map<String, Object> value, Logical vault) {
|
private LogicalResponse writeValue(String path, Map<String, Object> value, Logical vault) {
|
||||||
try {
|
try {
|
||||||
return vault.write(path, value);
|
return vault.write(path, value);
|
||||||
} catch (VaultException e) {
|
} catch (VaultException e) {
|
||||||
LOG.error("Failed to write value from vault: {}\n{}", path, e);
|
LOG.error("Failed to write value from vault: {}\n{}", path, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LogicalResponse readValue(String path, Logical v) {
|
private LogicalResponse readValue(String path, Logical v) {
|
||||||
try {
|
try {
|
||||||
return v.read(path);
|
return v.read(path);
|
||||||
} catch (VaultException e) {
|
} catch (VaultException e) {
|
||||||
LOG.error("Failed to read value from vault: {}\n{}", path, e);
|
LOG.error("Failed to read value from vault: {}\n{}", path, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,46 +1,46 @@
|
|||||||
package cz.moneta.test.harness.connectors;
|
package cz.moneta.test.harness.connectors;
|
||||||
|
|
||||||
import jakarta.xml.bind.JAXBContext;
|
import jakarta.xml.bind.JAXBContext;
|
||||||
import jakarta.xml.bind.JAXBException;
|
import jakarta.xml.bind.JAXBException;
|
||||||
import jakarta.xml.ws.BindingProvider;
|
import jakarta.xml.ws.BindingProvider;
|
||||||
import jakarta.xml.ws.Dispatch;
|
import jakarta.xml.ws.Dispatch;
|
||||||
import jakarta.xml.ws.Service;
|
import jakarta.xml.ws.Service;
|
||||||
import jakarta.xml.ws.handler.MessageContext;
|
import jakarta.xml.ws.handler.MessageContext;
|
||||||
import org.apache.cxf.jaxws.CXFService;
|
import org.apache.cxf.jaxws.CXFService;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
public class WsConnector {
|
public class WsConnector {
|
||||||
|
|
||||||
private final Service service;
|
private final Service service;
|
||||||
private final QName portName;
|
private final QName portName;
|
||||||
private final String address;
|
private final String address;
|
||||||
private final String serviceNamespace;
|
private final String serviceNamespace;
|
||||||
|
|
||||||
public WsConnector(String address, URL wsdlUrl, QName serviceName) {
|
public WsConnector(String address, URL wsdlUrl, QName serviceName) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
Service service = CXFService.create(wsdlUrl, serviceName);
|
Service service = CXFService.create(wsdlUrl, serviceName);
|
||||||
if (service.getPorts().hasNext()) {
|
if (service.getPorts().hasNext()) {
|
||||||
this.portName = service.getPorts().next();
|
this.portName = service.getPorts().next();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException(String.format("Service definition %s has no ports defined", wsdlUrl.toString()));
|
throw new IllegalStateException(String.format("Service definition %s has no ports defined", wsdlUrl.toString()));
|
||||||
}
|
}
|
||||||
this.serviceNamespace = serviceName.getNamespaceURI();
|
this.serviceNamespace = serviceName.getNamespaceURI();
|
||||||
this.service = service;
|
this.service = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <RESP> RESP invoke(Object request, Class<RESP> responseClass) {
|
public <RESP> RESP invoke(Object request, Class<RESP> responseClass) {
|
||||||
try {
|
try {
|
||||||
QName operationName = new QName(serviceNamespace, request.getClass().getSimpleName());
|
QName operationName = new QName(serviceNamespace, request.getClass().getSimpleName());
|
||||||
JAXBContext jaxbContext = JAXBContext.newInstance(responseClass.getPackage().getName());
|
JAXBContext jaxbContext = JAXBContext.newInstance(responseClass.getPackage().getName());
|
||||||
Dispatch<Object> dispatch = service.createDispatch(portName, jaxbContext, Service.Mode.PAYLOAD);
|
Dispatch<Object> dispatch = service.createDispatch(portName, jaxbContext, Service.Mode.PAYLOAD);
|
||||||
dispatch.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
|
dispatch.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
|
||||||
dispatch.getRequestContext().put(MessageContext.WSDL_OPERATION, operationName);
|
dispatch.getRequestContext().put(MessageContext.WSDL_OPERATION, operationName);
|
||||||
return (RESP) dispatch.invoke(request);
|
return (RESP) dispatch.invoke(request);
|
||||||
} catch (JAXBException e) {
|
} catch (JAXBException e) {
|
||||||
throw new IllegalArgumentException("Failed to invoke request", e);
|
throw new IllegalArgumentException("Failed to invoke request", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
package cz.moneta.test.harness.connectors.common;
|
package cz.moneta.test.harness.connectors.common;
|
||||||
|
|
||||||
import cz.moneta.test.harness.config.ConfigProvider;
|
import cz.moneta.test.harness.config.ConfigProvider;
|
||||||
import cz.moneta.test.harness.context.ConfigAccessor;
|
import cz.moneta.test.harness.context.ConfigAccessor;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ServletConfigAccessor implements ConfigAccessor {
|
public class ServletConfigAccessor implements ConfigAccessor {
|
||||||
|
|
||||||
private final Map<String, String> config = new ConcurrentHashMap<>(ConfigProvider.readConfig());
|
private final Map<String, String> config = new ConcurrentHashMap<>(ConfigProvider.readConfig());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfig(String key) {
|
public String getConfig(String key) {
|
||||||
return config.get(key);
|
return config.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfig(String key, String defaultValue) {
|
public String getConfig(String key, String defaultValue) {
|
||||||
return config.getOrDefault(key, defaultValue);
|
return config.getOrDefault(key, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putConfig(String key, String value) {
|
public void putConfig(String key, String value) {
|
||||||
config.put(key, value);
|
config.put(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,44 +1,44 @@
|
|||||||
package cz.moneta.test.harness.connectors.database;
|
package cz.moneta.test.harness.connectors.database;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.Connector;
|
import cz.moneta.test.harness.connectors.Connector;
|
||||||
import org.jooq.Record;
|
import org.jooq.Record;
|
||||||
import org.jooq.Result;
|
import org.jooq.Result;
|
||||||
import org.jooq.SQLDialect;
|
import org.jooq.SQLDialect;
|
||||||
import org.jooq.impl.DSL;
|
import org.jooq.impl.DSL;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public class DatabaseConnector implements Connector {
|
public class DatabaseConnector implements Connector {
|
||||||
|
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String user;
|
private final String user;
|
||||||
private final String password;
|
private final String password;
|
||||||
|
|
||||||
public DatabaseConnector(String url, String user, String password) {
|
public DatabaseConnector(String url, String user, String password) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Result<Record> executeSql(String sql, SQLDialect sqlDialect) {
|
protected Result<Record> executeSql(String sql, SQLDialect sqlDialect) {
|
||||||
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
||||||
return DSL.using(connection, sqlDialect).fetch(sql);
|
return DSL.using(connection, sqlDialect).fetch(sql);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error executing native sql database", e);
|
throw new RuntimeException("Error executing native sql database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUser() {
|
public String getUser() {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
package cz.moneta.test.harness.connectors.database;
|
package cz.moneta.test.harness.connectors.database;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.auth.Credentials;
|
import cz.moneta.test.harness.support.auth.Credentials;
|
||||||
import org.jooq.Record;
|
import org.jooq.Record;
|
||||||
import org.jooq.Result;
|
import org.jooq.Result;
|
||||||
import org.jooq.SQLDialect;
|
import org.jooq.SQLDialect;
|
||||||
|
|
||||||
public class MsSqlConnector extends DatabaseConnector {
|
public class MsSqlConnector extends DatabaseConnector {
|
||||||
|
|
||||||
private static final String windowsAuthenticationOption = ";integratedSecurity=true";
|
private static final String windowsAuthenticationOption = ";integratedSecurity=true";
|
||||||
|
|
||||||
public MsSqlConnector(String url, Credentials credentials) {
|
public MsSqlConnector(String url, Credentials credentials) {
|
||||||
super(url, credentials.getUsername(), credentials.getPassword());
|
super(url, credentials.getUsername(), credentials.getPassword());
|
||||||
}
|
}
|
||||||
|
|
||||||
public MsSqlConnector(String url) {
|
public MsSqlConnector(String url) {
|
||||||
super(url + windowsAuthenticationOption, "", "");
|
super(url + windowsAuthenticationOption, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Record> executeSql(String sql) {
|
public Result<Record> executeSql(String sql) {
|
||||||
return super.executeSql(sql, SQLDialect.SQLSERVER);
|
return super.executeSql(sql, SQLDialect.SQLSERVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,65 +1,65 @@
|
|||||||
package cz.moneta.test.harness.connectors.database;
|
package cz.moneta.test.harness.connectors.database;
|
||||||
|
|
||||||
import org.jooq.Configuration;
|
import org.jooq.Configuration;
|
||||||
import org.jooq.DSLContext;
|
import org.jooq.DSLContext;
|
||||||
import org.jooq.Record;
|
import org.jooq.Record;
|
||||||
import org.jooq.Result;
|
import org.jooq.Result;
|
||||||
import org.jooq.Routine;
|
import org.jooq.Routine;
|
||||||
import org.jooq.SQLDialect;
|
import org.jooq.SQLDialect;
|
||||||
import org.jooq.TableRecord;
|
import org.jooq.TableRecord;
|
||||||
import org.jooq.impl.DSL;
|
import org.jooq.impl.DSL;
|
||||||
import org.jooq.impl.DefaultConfiguration;
|
import org.jooq.impl.DefaultConfiguration;
|
||||||
import org.jooq.impl.DefaultConnectionProvider;
|
import org.jooq.impl.DefaultConnectionProvider;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class OracleConnector extends DatabaseConnector {
|
public class OracleConnector extends DatabaseConnector {
|
||||||
|
|
||||||
public OracleConnector(String url, String user, String password) {
|
public OracleConnector(String url, String user, String password) {
|
||||||
super(url, user, password);
|
super(url, user, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R extends TableRecord<R>> R executeDsl(Function<DSLContext, R> query) {
|
public <R extends TableRecord<R>> R executeDsl(Function<DSLContext, R> query) {
|
||||||
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
||||||
return query.apply(DSL.using(connection, SQLDialect.ORACLE));
|
return query.apply(DSL.using(connection, SQLDialect.ORACLE));
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error querying database", e);
|
throw new RuntimeException("Error querying database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <K> K insertDsl(Function<DSLContext, K> query) {
|
public <K> K insertDsl(Function<DSLContext, K> query) {
|
||||||
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
||||||
return query.apply(DSL.using(connection, SQLDialect.ORACLE));
|
return query.apply(DSL.using(connection, SQLDialect.ORACLE));
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error querying database", e);
|
throw new RuntimeException("Error querying database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R extends Routine<?>> R executeProcedure(R procedure) {
|
public <R extends Routine<?>> R executeProcedure(R procedure) {
|
||||||
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
||||||
Configuration configuration = new DefaultConfiguration()
|
Configuration configuration = new DefaultConfiguration()
|
||||||
.derive(new DefaultConnectionProvider(connection))
|
.derive(new DefaultConnectionProvider(connection))
|
||||||
.derive(SQLDialect.ORACLE);
|
.derive(SQLDialect.ORACLE);
|
||||||
procedure.execute(configuration);
|
procedure.execute(configuration);
|
||||||
return procedure;
|
return procedure;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error executing procedure database", e);
|
throw new RuntimeException("Error executing procedure database", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Record> executeSql(String sql) {
|
public Result<Record> executeSql(String sql) {
|
||||||
return super.executeSql(sql, SQLDialect.ORACLE);
|
return super.executeSql(sql, SQLDialect.ORACLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeStatement(Consumer<Connection> execute) {
|
public void executeStatement(Consumer<Connection> execute) {
|
||||||
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
try (Connection connection = DriverManager.getConnection(getUrl(), getUser(), getPassword())) {
|
||||||
execute.accept(connection);
|
execute.accept(connection);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error executing statement", e);
|
throw new RuntimeException("Error executing statement", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
package cz.moneta.test.harness.connectors.database;
|
package cz.moneta.test.harness.connectors.database;
|
||||||
|
|
||||||
import org.jooq.Record;
|
import org.jooq.Record;
|
||||||
import org.jooq.Result;
|
import org.jooq.Result;
|
||||||
import org.jooq.SQLDialect;
|
import org.jooq.SQLDialect;
|
||||||
|
|
||||||
public class PostgresConnector extends DatabaseConnector {
|
public class PostgresConnector extends DatabaseConnector {
|
||||||
|
|
||||||
public PostgresConnector(String url, String user, String password) {
|
public PostgresConnector(String url, String user, String password) {
|
||||||
super(url, user, password);
|
super(url, user, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Record> executeSql(String sqlToExecute) {
|
public Result<Record> executeSql(String sqlToExecute) {
|
||||||
return super.executeSql(sqlToExecute, SQLDialect.POSTGRES);
|
return super.executeSql(sqlToExecute, SQLDialect.POSTGRES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,23 @@
|
|||||||
package cz.moneta.test.harness.connectors.messaging;
|
package cz.moneta.test.harness.connectors.messaging;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.jms.BytesMessage;
|
|
||||||
import javax.jms.JMSConsumer;
|
|
||||||
import javax.jms.JMSContext;
|
|
||||||
import javax.jms.JMSException;
|
|
||||||
import javax.jms.JMSRuntimeException;
|
|
||||||
import javax.jms.Message;
|
|
||||||
import javax.jms.TextMessage;
|
|
||||||
import javax.net.ssl.KeyManagerFactory;
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
@ -27,26 +26,28 @@ import javax.net.ssl.TrustManagerFactory;
|
|||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.ibm.mq.jms.MQConnectionFactory;
|
import com.ibm.mq.MQException;
|
||||||
|
import com.ibm.mq.MQGetMessageOptions;
|
||||||
|
import com.ibm.mq.MQMessage;
|
||||||
|
import com.ibm.mq.MQPutMessageOptions;
|
||||||
|
import com.ibm.mq.MQQueue;
|
||||||
|
import com.ibm.mq.MQQueueManager;
|
||||||
|
import com.ibm.mq.constants.CMQC;
|
||||||
import com.ibm.msg.client.wmq.WMQConstants;
|
import com.ibm.msg.client.wmq.WMQConstants;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.Connector;
|
import cz.moneta.test.harness.connectors.Connector;
|
||||||
import cz.moneta.test.harness.messaging.MessageContentType;
|
|
||||||
import cz.moneta.test.harness.messaging.MqMessageFormat;
|
|
||||||
import cz.moneta.test.harness.messaging.ReceivedMessage;
|
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingConnectionException;
|
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingDestinationException;
|
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
|
||||||
import cz.moneta.test.harness.support.messaging.ImqRequest;
|
import cz.moneta.test.harness.support.messaging.ImqRequest;
|
||||||
|
import cz.moneta.test.harness.support.messaging.MessageContentType;
|
||||||
|
import cz.moneta.test.harness.support.messaging.MqMessageFormat;
|
||||||
|
import cz.moneta.test.harness.support.messaging.ReceivedMessage;
|
||||||
|
import cz.moneta.test.harness.support.messaging.exception.MessagingConnectionException;
|
||||||
|
import cz.moneta.test.harness.support.messaging.exception.MessagingDestinationException;
|
||||||
|
import cz.moneta.test.harness.support.messaging.exception.MessagingTimeoutException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IBM MQ connector using JMS client with Jakarta JMS API. Supports
|
* IBM MQ connector using the native com.ibm.mq classes. Supports
|
||||||
* multi-instance Queue Manager, SSL/TLS, and multiple message formats.
|
* multi-instance Queue Manager connection lists, SSL/TLS, message properties,
|
||||||
* <p>
|
* and JSON/XML/UTF-8/EBCDIC payload formats.
|
||||||
* Supported formats: - JSON: JMS TextMessage with plain JSON string (default) -
|
|
||||||
* XML: JMS TextMessage with XML string - UTF-8 (CCSID 1208): JMS BytesMessage
|
|
||||||
* with UTF-8 encoding - EBCDIC (CCSID 870): JMS BytesMessage with EBCDIC
|
|
||||||
* IBM-870 encoding
|
|
||||||
*/
|
*/
|
||||||
public class IbmMqConnector implements Connector {
|
public class IbmMqConnector implements Connector {
|
||||||
|
|
||||||
@ -59,12 +60,19 @@ public class IbmMqConnector implements Connector {
|
|||||||
private static final long DEFAULT_MAX_POLL_INTERVAL_MS = 1000;
|
private static final long DEFAULT_MAX_POLL_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
private static final String TLS_VERSION = "TLSv1.2";
|
private static final String TLS_VERSION = "TLSv1.2";
|
||||||
|
private static final String JMS_ID_PREFIX = "ID:";
|
||||||
|
private static final Pattern SELECTOR_EQUALS_PATTERN = Pattern
|
||||||
|
.compile("\\s*([A-Za-z_][A-Za-z0-9_.-]*)\\s*=\\s*'((?:''|[^'])*)'\\s*");
|
||||||
|
|
||||||
private final MQConnectionFactory connectionFactory;
|
private final String connectionNameList;
|
||||||
private JMSContext jmsContext;
|
private final String channel;
|
||||||
private final String queueManager;
|
private final String queueManager;
|
||||||
private final String user;
|
private final String user;
|
||||||
private final String password;
|
private final String password;
|
||||||
|
private final SSLSocketFactory sslSocketFactory;
|
||||||
|
private final String sslCipherSuite;
|
||||||
|
|
||||||
|
private MQQueueManager mqQueueManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor with multi-instance Queue Manager support.
|
* Constructor with multi-instance Queue Manager support.
|
||||||
@ -82,35 +90,17 @@ public class IbmMqConnector implements Connector {
|
|||||||
*/
|
*/
|
||||||
public IbmMqConnector(String connectionNameList, String channel, String queueManager, String user, String password,
|
public IbmMqConnector(String connectionNameList, String channel, String queueManager, String user, String password,
|
||||||
String keystorePath, String keystorePassword, String sslCipherSuite) {
|
String keystorePath, String keystorePassword, String sslCipherSuite) {
|
||||||
|
this.connectionNameList = connectionNameList;
|
||||||
|
this.channel = channel;
|
||||||
this.queueManager = queueManager;
|
this.queueManager = queueManager;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
|
this.sslCipherSuite = sslCipherSuite;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
connectionFactory = new MQConnectionFactory();
|
this.sslSocketFactory = keystorePath != null && !keystorePath.isBlank() && keystorePassword != null
|
||||||
connectionFactory.setConnectionNameList(connectionNameList);
|
&& !keystorePassword.isBlank() ? getSslSocketFactory(keystorePath, keystorePassword) : null;
|
||||||
connectionFactory.setQueueManager(queueManager);
|
|
||||||
connectionFactory.setChannel(channel);
|
|
||||||
connectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
|
|
||||||
if (user != null && !user.isBlank()) {
|
|
||||||
connectionFactory.setStringProperty(WMQConstants.USERID, user);
|
|
||||||
}
|
|
||||||
if (password != null && !password.isBlank()) {
|
|
||||||
connectionFactory.setStringProperty(WMQConstants.PASSWORD, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keystorePath != null && !keystorePath.isBlank() && keystorePassword != null
|
|
||||||
&& !keystorePassword.isBlank()) {
|
|
||||||
connectionFactory.setSSLSocketFactory(getSslSocketFactory(keystorePath, keystorePassword));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sslCipherSuite != null && !sslCipherSuite.isBlank()) {
|
|
||||||
connectionFactory.setSSLCipherSuite(sslCipherSuite);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize JMS context
|
|
||||||
connect();
|
connect();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new MessagingConnectionException("Failed to create IBM MQ connection to " + queueManager, e);
|
throw new MessagingConnectionException("Failed to create IBM MQ connection to " + queueManager, e);
|
||||||
}
|
}
|
||||||
@ -121,97 +111,82 @@ public class IbmMqConnector implements Connector {
|
|||||||
*/
|
*/
|
||||||
private void connect() {
|
private void connect() {
|
||||||
try {
|
try {
|
||||||
this.jmsContext = connectionFactory.createContext(user, password, JMSContext.AUTO_ACKNOWLEDGE);
|
this.mqQueueManager = new MQQueueManager(queueManager, createConnectionProperties());
|
||||||
this.jmsContext.start();
|
|
||||||
LOG.info("Connected to IBM MQ: {}", queueManager);
|
LOG.info("Connected to IBM MQ: {}", queueManager);
|
||||||
} catch (Exception e) {
|
} catch (MQException e) {
|
||||||
throw new MessagingConnectionException(
|
throw new MessagingConnectionException(
|
||||||
"Failed to connect to IBM MQ: " + queueManager + " - " + e.getMessage(), e);
|
"Failed to connect to IBM MQ: " + queueManager + " - " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Hashtable<String, Object> createConnectionProperties() {
|
||||||
* Send a JSON or XML message as TextMessage.
|
Hashtable<String, Object> properties = new Hashtable<>();
|
||||||
*/
|
if (connectionNameList != null && !connectionNameList.isBlank()) {
|
||||||
private void sendTextMessage(String queueName, String payload, Map<String, String> properties) {
|
properties.put(WMQConstants.WMQ_CONNECTION_NAME_LIST, connectionNameList);
|
||||||
javax.jms.Queue queue = getQueue(queueName);
|
}
|
||||||
|
properties.put(CMQC.CHANNEL_PROPERTY, channel);
|
||||||
|
properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_CLIENT);
|
||||||
|
|
||||||
TextMessage message = jmsContext.createTextMessage(payload);
|
if (user != null && !user.isBlank()) {
|
||||||
|
properties.put(CMQC.USER_ID_PROPERTY, user);
|
||||||
// Set JMS properties
|
}
|
||||||
if (properties != null) {
|
if (password != null && !password.isBlank()) {
|
||||||
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
properties.put(CMQC.PASSWORD_PROPERTY, password);
|
||||||
try {
|
}
|
||||||
if (entry.getKey().equals(ImqRequest.PROP_JMS_CORRELATION_ID)) {
|
if (sslSocketFactory != null) {
|
||||||
message.setJMSCorrelationID(entry.getValue());
|
properties.put(CMQC.SSL_SOCKET_FACTORY_PROPERTY, sslSocketFactory);
|
||||||
continue;
|
}
|
||||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_TYPE)) {
|
if (sslCipherSuite != null && !sslCipherSuite.isBlank()) {
|
||||||
message.setJMSType(entry.getValue());
|
properties.put(CMQC.SSL_CIPHER_SUITE_PROPERTY, sslCipherSuite);
|
||||||
continue;
|
|
||||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_MESSAGE_ID)) {
|
|
||||||
message.setJMSMessageID(entry.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
message.setStringProperty(entry.getKey(), entry.getValue());
|
|
||||||
} catch (JMSException e) {
|
|
||||||
LOG.warn("Failed to set property: {}", entry.getKey(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return properties;
|
||||||
jmsContext.createProducer().send(queue, message);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw new MessagingDestinationException("Failed to send message to queue: " + queueName, e);
|
|
||||||
}
|
|
||||||
LOG.debug("Sent JSON/XML message to queue: {}", queueName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message as BytesMessage with specific encoding and CCSID.
|
* Send a JSON or XML message as MQ string message.
|
||||||
|
*/
|
||||||
|
private void sendTextMessage(String queueName, String payload, Map<String, String> properties) {
|
||||||
|
sendMessage(queueName, payload.getBytes(UTF_8), UTF_8, 1208, CMQC.MQFMT_STRING, properties, "JSON/XML");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message as raw bytes with specific encoding and CCSID.
|
||||||
*/
|
*/
|
||||||
private void sendBytesMessage(String queueName, String payload, Charset charset, int ccsid,
|
private void sendBytesMessage(String queueName, String payload, Charset charset, int ccsid,
|
||||||
Map<String, String> properties) {
|
Map<String, String> properties) {
|
||||||
javax.jms.Queue queue = getQueue(queueName);
|
sendMessage(queueName, payload.getBytes(charset), charset, ccsid, CMQC.MQFMT_NONE, properties,
|
||||||
|
charset.displayName());
|
||||||
|
}
|
||||||
|
|
||||||
BytesMessage message = jmsContext.createBytesMessage();
|
private void sendMessage(String queueName, byte[] payload, Charset charset, int ccsid, String mqFormat,
|
||||||
|
Map<String, String> properties, String logFormat) {
|
||||||
// Convert payload to bytes using specified charset
|
MQQueue queue = null;
|
||||||
byte[] bytes = payload.getBytes(charset);
|
|
||||||
try {
|
try {
|
||||||
message.writeBytes(bytes);
|
int openOptions = CMQC.MQOO_OUTPUT | CMQC.MQOO_FAIL_IF_QUIESCING;
|
||||||
message.setIntProperty("CCSID", ccsid);
|
|
||||||
} catch (JMSException e) {
|
|
||||||
throw new MessagingDestinationException("Failed to create bytes message", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set JMS properties
|
queue = openQueue(queueName, openOptions);
|
||||||
if (properties != null) {
|
|
||||||
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
MQMessage message = new MQMessage();
|
||||||
try {
|
message.format = mqFormat;
|
||||||
if (entry.getKey().equals(ImqRequest.PROP_JMS_CORRELATION_ID)) {
|
message.characterSet = ccsid;
|
||||||
message.setJMSCorrelationID(entry.getValue());
|
message.write(payload);
|
||||||
continue;
|
if (!CMQC.MQFMT_STRING.equals(mqFormat)) {
|
||||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_TYPE)) {
|
message.setIntProperty("CCSID", ccsid);
|
||||||
message.setJMSType(entry.getValue());
|
|
||||||
continue;
|
|
||||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_MESSAGE_ID)) {
|
|
||||||
message.setJMSMessageID(entry.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
message.setStringProperty(entry.getKey(), entry.getValue());
|
|
||||||
} catch (JMSException e) {
|
|
||||||
LOG.warn("Failed to set property: {}", entry.getKey(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MQPutMessageOptions putOptions = new MQPutMessageOptions();
|
||||||
|
putOptions.options = CMQC.MQPMO_NO_SYNCPOINT | CMQC.MQPMO_FAIL_IF_QUIESCING;
|
||||||
|
applyMessageProperties(message, properties);
|
||||||
|
|
||||||
|
queue.put(message, putOptions);
|
||||||
|
} catch (MQException | IOException e) {
|
||||||
|
throw new MessagingDestinationException("Failed to send message to queue: " + queueName, e);
|
||||||
|
} finally {
|
||||||
|
closeQueue(queue, queueName);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
LOG.debug("Sent {} message ({}) to queue: {}", logFormat, charset, queueName);
|
||||||
jmsContext.createProducer().send(queue, message);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw new MessagingDestinationException("Failed to send message to queue: " + queueName, e);
|
|
||||||
}
|
|
||||||
LOG.debug("Sent {} message to queue: {}", charset, queueName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -220,7 +195,7 @@ public class IbmMqConnector implements Connector {
|
|||||||
* @param queueName Queue name
|
* @param queueName Queue name
|
||||||
* @param payload Message payload
|
* @param payload Message payload
|
||||||
* @param format Message format (JSON, XML, EBCDIC_870, UTF8_1208)
|
* @param format Message format (JSON, XML, EBCDIC_870, UTF8_1208)
|
||||||
* @param properties JMS properties to set
|
* @param properties message properties to set
|
||||||
*/
|
*/
|
||||||
public void send(String queueName, String payload, MqMessageFormat format, Map<String, String> properties) {
|
public void send(String queueName, String payload, MqMessageFormat format, Map<String, String> properties) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
@ -234,56 +209,99 @@ public class IbmMqConnector implements Connector {
|
|||||||
* Receive a message from a queue with timeout.
|
* Receive a message from a queue with timeout.
|
||||||
*
|
*
|
||||||
* @param queueName Queue name
|
* @param queueName Queue name
|
||||||
* @param messageSelector JMS message selector (optional)
|
* @param messageSelector JMS-style message selector (optional). Equality
|
||||||
|
* predicates joined by AND are evaluated client-side.
|
||||||
* @param format Expected message format
|
* @param format Expected message format
|
||||||
* @param timeout Timeout duration
|
* @param timeout Timeout duration
|
||||||
* @return Received message
|
* @return Received message
|
||||||
*/
|
*/
|
||||||
public ReceivedMessage receive(String queueName, String messageSelector, MqMessageFormat format,
|
public ReceivedMessage receive(String queueName, String messageSelector, MqMessageFormat format, Duration timeout) {
|
||||||
java.time.Duration timeout) {
|
if (messageSelector == null || messageSelector.isBlank()) {
|
||||||
long timeoutMs = timeout.toMillis();
|
return receiveNext(queueName, format, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
javax.jms.Queue queue = getQueue(queueName);
|
return receiveMatching(queueName, messageSelector, format, timeout);
|
||||||
JMSConsumer consumer = (messageSelector == null || messageSelector.isBlank() ? jmsContext.createConsumer(queue)
|
}
|
||||||
: jmsContext.createConsumer(queue, messageSelector));
|
|
||||||
|
|
||||||
AtomicBoolean messageFound = new AtomicBoolean(false);
|
|
||||||
ReceivedMessage received = null;
|
|
||||||
|
|
||||||
long pollInterval = DEFAULT_POLL_INTERVAL_MS;
|
|
||||||
long remainingTime = timeoutMs;
|
|
||||||
|
|
||||||
|
private ReceivedMessage receiveNext(String queueName, MqMessageFormat format, Duration timeout) {
|
||||||
|
MQQueue queue = null;
|
||||||
try {
|
try {
|
||||||
while (remainingTime > 0 && !messageFound.get()) {
|
queue = openQueue(queueName, CMQC.MQOO_INPUT_SHARED | CMQC.MQOO_FAIL_IF_QUIESCING);
|
||||||
Message message = consumer.receive(remainingTime);
|
MQMessage message = new MQMessage();
|
||||||
|
MQGetMessageOptions getOptions = new MQGetMessageOptions();
|
||||||
|
getOptions.options = CMQC.MQGMO_WAIT | CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
|
getOptions.waitInterval = toWaitInterval(timeout.toMillis());
|
||||||
|
|
||||||
if (message != null) {
|
queue.get(message, getOptions);
|
||||||
received = decodeMessage(message, queueName, format);
|
return decodeMessage(message, queueName, format);
|
||||||
messageFound.set(true);
|
} catch (MQException e) {
|
||||||
} else {
|
if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) {
|
||||||
// Exponential backoff
|
|
||||||
pollInterval = Math.min(pollInterval * 2, DEFAULT_MAX_POLL_INTERVAL_MS);
|
|
||||||
remainingTime -= pollInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (received == null) {
|
|
||||||
throw new MessagingTimeoutException("No message matching filter found on queue '" + queueName
|
throw new MessagingTimeoutException("No message matching filter found on queue '" + queueName
|
||||||
+ "' within " + timeout.toMillis() + "ms");
|
+ "' within " + timeout.toMillis() + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
return received;
|
|
||||||
|
|
||||||
} catch (MessagingTimeoutException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new MessagingDestinationException("Failed to receive message from queue: " + queueName, e);
|
throw new MessagingDestinationException("Failed to receive message from queue: " + queueName, e);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
closeQueue(queue, queueName);
|
||||||
consumer.close();
|
}
|
||||||
} catch (JMSRuntimeException e) {
|
}
|
||||||
LOG.warn("Failed to close consumer", e);
|
|
||||||
|
private ReceivedMessage receiveMatching(String queueName, String messageSelector, MqMessageFormat format,
|
||||||
|
Duration timeout) {
|
||||||
|
long deadline = System.currentTimeMillis() + timeout.toMillis();
|
||||||
|
long pollInterval = DEFAULT_POLL_INTERVAL_MS;
|
||||||
|
Selector selector = Selector.parse(messageSelector);
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() < deadline) {
|
||||||
|
ReceivedMessage received = tryReceiveMatching(queueName, format, selector);
|
||||||
|
if (received != null) {
|
||||||
|
return received;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sleepQuietly(Math.min(pollInterval, Math.max(1, deadline - System.currentTimeMillis())));
|
||||||
|
pollInterval = Math.min(pollInterval * 2, DEFAULT_MAX_POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MessagingTimeoutException("No message matching filter found on queue '" + queueName + "' within "
|
||||||
|
+ timeout.toMillis() + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReceivedMessage tryReceiveMatching(String queueName, MqMessageFormat format, Selector selector) {
|
||||||
|
MQQueue queue = null;
|
||||||
|
try {
|
||||||
|
queue = openQueue(queueName,
|
||||||
|
CMQC.MQOO_BROWSE | CMQC.MQOO_INPUT_SHARED | CMQC.MQOO_FAIL_IF_QUIESCING);
|
||||||
|
|
||||||
|
MQGetMessageOptions browseOptions = new MQGetMessageOptions();
|
||||||
|
browseOptions.options = CMQC.MQGMO_BROWSE_FIRST | CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
MQMessage browsedMessage = new MQMessage();
|
||||||
|
try {
|
||||||
|
queue.get(browsedMessage, browseOptions);
|
||||||
|
} catch (MQException e) {
|
||||||
|
if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReceivedMessage browsed = decodeMessage(browsedMessage, queueName, format);
|
||||||
|
if (selector.matches(browsed.getHeaders())) {
|
||||||
|
MQMessage removedMessage = new MQMessage();
|
||||||
|
MQGetMessageOptions removeOptions = new MQGetMessageOptions();
|
||||||
|
removeOptions.options = CMQC.MQGMO_MSG_UNDER_CURSOR | CMQC.MQGMO_NO_SYNCPOINT
|
||||||
|
| CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
|
queue.get(removedMessage, removeOptions);
|
||||||
|
return decodeMessage(removedMessage, queueName, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
browseOptions.options = CMQC.MQGMO_BROWSE_NEXT | CMQC.MQGMO_NO_SYNCPOINT
|
||||||
|
| CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
|
}
|
||||||
|
} catch (MQException | RuntimeException e) {
|
||||||
|
throw new MessagingDestinationException("Failed to receive message from queue: " + queueName, e);
|
||||||
|
} finally {
|
||||||
|
closeQueue(queue, queueName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +309,8 @@ public class IbmMqConnector implements Connector {
|
|||||||
* Browse a queue (non-destructive read).
|
* Browse a queue (non-destructive read).
|
||||||
*
|
*
|
||||||
* @param queueName Queue name
|
* @param queueName Queue name
|
||||||
* @param messageSelector JMS message selector (optional)
|
* @param messageSelector JMS-style message selector (optional). Equality
|
||||||
|
* predicates joined by AND are evaluated client-side.
|
||||||
* @param format Expected message format
|
* @param format Expected message format
|
||||||
* @param maxMessages Maximum number of messages to browse
|
* @param maxMessages Maximum number of messages to browse
|
||||||
* @return List of received messages
|
* @return List of received messages
|
||||||
@ -299,170 +318,268 @@ public class IbmMqConnector implements Connector {
|
|||||||
public List<ReceivedMessage> browse(String queueName, String messageSelector, MqMessageFormat format,
|
public List<ReceivedMessage> browse(String queueName, String messageSelector, MqMessageFormat format,
|
||||||
int maxMessages) {
|
int maxMessages) {
|
||||||
List<ReceivedMessage> messages = new ArrayList<>();
|
List<ReceivedMessage> messages = new ArrayList<>();
|
||||||
javax.jms.Queue queue = getQueue(queueName);
|
Selector selector = Selector.parse(messageSelector);
|
||||||
|
MQQueue queue = null;
|
||||||
|
|
||||||
try (javax.jms.QueueBrowser browser = (messageSelector == null || messageSelector.isBlank())
|
try {
|
||||||
? jmsContext.createBrowser(queue)
|
queue = openQueue(queueName, CMQC.MQOO_BROWSE | CMQC.MQOO_FAIL_IF_QUIESCING);
|
||||||
: jmsContext.createBrowser(queue, messageSelector)) {
|
|
||||||
|
|
||||||
Enumeration<?> enumeration = browser.getEnumeration();
|
MQGetMessageOptions browseOptions = new MQGetMessageOptions();
|
||||||
int count = 0;
|
browseOptions.options = CMQC.MQGMO_BROWSE_FIRST | CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
|
|
||||||
while (enumeration.hasMoreElements() && count < maxMessages) {
|
while (messages.size() < maxMessages) {
|
||||||
Message message = (Message) enumeration.nextElement();
|
MQMessage message = new MQMessage();
|
||||||
if (message != null) {
|
try {
|
||||||
ReceivedMessage received = decodeMessage(message, queueName, format);
|
queue.get(message, browseOptions);
|
||||||
messages.add(received);
|
} catch (MQException e) {
|
||||||
count++;
|
if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReceivedMessage received = decodeMessage(message, queueName, format);
|
||||||
|
if (selector.matches(received.getHeaders())) {
|
||||||
|
messages.add(received);
|
||||||
|
}
|
||||||
|
|
||||||
|
browseOptions.options = CMQC.MQGMO_BROWSE_NEXT | CMQC.MQGMO_NO_SYNCPOINT
|
||||||
|
| CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages;
|
return messages;
|
||||||
} catch (JMSException e) {
|
} catch (MQException e) {
|
||||||
throw new MessagingDestinationException("Failed to browse queue: " + queueName, e);
|
throw new MessagingDestinationException("Failed to browse queue: " + queueName, e);
|
||||||
|
} finally {
|
||||||
|
closeQueue(queue, queueName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a JMS message to ReceivedMessage.
|
* Decode an IBM MQ message to ReceivedMessage.
|
||||||
*/
|
*/
|
||||||
private ReceivedMessage decodeMessage(Message jmsMessage, String queueName, MqMessageFormat format) {
|
private ReceivedMessage decodeMessage(MQMessage mqMessage, String queueName, MqMessageFormat format) {
|
||||||
long timestamp;
|
long timestamp = mqMessage.putDateTime != null ? mqMessage.putDateTime.getTimeInMillis()
|
||||||
try {
|
: System.currentTimeMillis();
|
||||||
timestamp = jmsMessage.getJMSTimestamp();
|
|
||||||
} catch (JMSException e) {
|
|
||||||
timestamp = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
if (timestamp == 0) {
|
if (timestamp == 0) {
|
||||||
timestamp = System.currentTimeMillis();
|
timestamp = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
extractMqHeadersAndProperties(mqMessage, headers, queueName);
|
||||||
|
|
||||||
|
byte[] data = readMessageBody(mqMessage);
|
||||||
String body;
|
String body;
|
||||||
MessageContentType contentType;
|
MessageContentType contentType;
|
||||||
Map<String, String> headers = new HashMap<>();
|
|
||||||
|
|
||||||
// Extract JMS properties as headers
|
switch (format) {
|
||||||
extractJmsProperties(jmsMessage, headers);
|
case XML -> {
|
||||||
|
body = new String(data, charsetFor(mqMessage.characterSet, UTF_8));
|
||||||
if (jmsMessage instanceof TextMessage textMessage) {
|
contentType = MessageContentType.XML;
|
||||||
try {
|
}
|
||||||
body = textMessage.getText();
|
case JSON -> {
|
||||||
} catch (JMSException e) {
|
body = new String(data, charsetFor(mqMessage.characterSet, UTF_8));
|
||||||
throw new RuntimeException("Failed to read text message body", e);
|
contentType = MessageContentType.JSON;
|
||||||
}
|
}
|
||||||
contentType = switch (format) {
|
case EBCDIC_870 -> {
|
||||||
case XML -> MessageContentType.XML;
|
body = new String(data, EBCDIC_870);
|
||||||
default -> MessageContentType.JSON;
|
|
||||||
};
|
|
||||||
} else if (jmsMessage instanceof BytesMessage bytesMessage) {
|
|
||||||
int ccsid;
|
|
||||||
try {
|
|
||||||
ccsid = bytesMessage.getIntProperty("CCSID");
|
|
||||||
} catch (JMSException e) {
|
|
||||||
ccsid = 1208; // default UTF-8
|
|
||||||
}
|
|
||||||
body = decodeBytesMessage(bytesMessage, ccsid);
|
|
||||||
contentType = MessageContentType.RAW_TEXT;
|
contentType = MessageContentType.RAW_TEXT;
|
||||||
} else {
|
}
|
||||||
try {
|
case UTF8_1208 -> {
|
||||||
throw new IllegalArgumentException("Unsupported message type: " + jmsMessage.getJMSType());
|
body = new String(data, UTF_8);
|
||||||
} catch (JMSException e) {
|
contentType = MessageContentType.RAW_TEXT;
|
||||||
throw new IllegalArgumentException("Unsupported message type", e);
|
}
|
||||||
}
|
default -> {
|
||||||
|
body = new String(data, UTF_8);
|
||||||
|
contentType = MessageContentType.RAW_TEXT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ReceivedMessage(body, contentType, headers, timestamp, queueName, null);
|
return new ReceivedMessage(body, contentType, headers, timestamp, queueName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private byte[] readMessageBody(MQMessage message) {
|
||||||
* Decode BytesMessage body based on CCSID.
|
|
||||||
*/
|
|
||||||
private String decodeBytesMessage(BytesMessage bytesMessage, int ccsid) {
|
|
||||||
try {
|
try {
|
||||||
long bodyLength;
|
message.seek(0);
|
||||||
try {
|
int length = message.getMessageLength();
|
||||||
bodyLength = bytesMessage.getBodyLength();
|
byte[] data = new byte[length];
|
||||||
} catch (JMSException e) {
|
message.readFully(data);
|
||||||
throw new RuntimeException("Failed to get message body length", e);
|
return data;
|
||||||
}
|
} catch (EOFException e) {
|
||||||
byte[] data = new byte[(int) bodyLength];
|
throw new RuntimeException("Failed to seek message body", e);
|
||||||
bytesMessage.readBytes(data);
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to read message body", e);
|
||||||
Charset charset = switch (ccsid) {
|
|
||||||
case 870 -> EBCDIC_870;
|
|
||||||
case 1208 -> UTF_8;
|
|
||||||
default -> UTF_8;
|
|
||||||
};
|
|
||||||
|
|
||||||
return new String(data, charset);
|
|
||||||
} catch (JMSException e) {
|
|
||||||
throw new RuntimeException("Failed to read BytesMessage body", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract JMS properties as headers.
|
* Extract MQ headers and message properties as headers.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
private void extractMqHeadersAndProperties(MQMessage message, Map<String, String> headers, String queueName) {
|
||||||
private void extractJmsProperties(Message message, Map<String, String> headers) {
|
headers.put("JMSMessageID", toJmsId(message.messageId));
|
||||||
try {
|
headers.put("JMSCorrelationID", toJmsId(message.correlationId));
|
||||||
// Common JMS headers
|
headers.put("JMSType", getStringProperty(message, ImqRequest.PROP_JMS_TYPE, ""));
|
||||||
headers.put("JMSMessageID", message.getJMSMessageID());
|
headers.put("JMSDestination", queueName);
|
||||||
try {
|
headers.put("JMSDeliveryMode", String.valueOf(message.persistence));
|
||||||
headers.put("JMSType", message.getJMSType() != null ? message.getJMSType() : "");
|
headers.put("JMSPriority", String.valueOf(message.priority));
|
||||||
} catch (JMSException e) {
|
headers.put("JMSTimestamp",
|
||||||
headers.put("JMSType", "");
|
message.putDateTime != null ? String.valueOf(message.putDateTime.getTimeInMillis()) : "");
|
||||||
}
|
headers.put("MQFormat", message.format != null ? message.format.trim() : "");
|
||||||
try {
|
headers.put("MQCharacterSet", String.valueOf(message.characterSet));
|
||||||
headers.put("JMSDestination",
|
headers.put("MQEncoding", String.valueOf(message.encoding));
|
||||||
message.getJMSDestination() != null ? message.getJMSDestination().toString() : "");
|
headers.put("MQBackoutCount", String.valueOf(message.backoutCount));
|
||||||
} catch (JMSException e) {
|
headers.put("MQReplyToQueue", message.replyToQueueName != null ? message.replyToQueueName.trim() : "");
|
||||||
headers.put("JMSDestination", "");
|
headers.put("MQReplyToQueueManager",
|
||||||
}
|
message.replyToQueueManagerName != null ? message.replyToQueueManagerName.trim() : "");
|
||||||
try {
|
headers.put("MQUserId", message.userId != null ? message.userId.trim() : "");
|
||||||
headers.put("JMSDeliveryMode", String.valueOf(message.getJMSDeliveryMode()));
|
|
||||||
} catch (JMSException e) {
|
|
||||||
headers.put("JMSDeliveryMode", "");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
headers.put("JMSPriority", String.valueOf(message.getJMSPriority()));
|
|
||||||
} catch (JMSException e) {
|
|
||||||
headers.put("JMSPriority", "");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
headers.put("JMSTimestamp", String.valueOf(message.getJMSTimestamp()));
|
|
||||||
} catch (JMSException e) {
|
|
||||||
headers.put("JMSTimestamp", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract custom properties
|
Enumeration<String> propertyNames;
|
||||||
Enumeration<String> propertyNames = (Enumeration<String>) message.getPropertyNames();
|
try {
|
||||||
while (propertyNames.hasMoreElements()) {
|
propertyNames = message.getPropertyNames("%");
|
||||||
String propName = propertyNames.nextElement();
|
} catch (MQException e) {
|
||||||
|
LOG.warn("Failed to extract MQ properties", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (propertyNames.hasMoreElements()) {
|
||||||
|
String propName = propertyNames.nextElement();
|
||||||
|
try {
|
||||||
Object propValue = message.getObjectProperty(propName);
|
Object propValue = message.getObjectProperty(propName);
|
||||||
if (propValue != null) {
|
if (propValue != null) {
|
||||||
headers.put(propName, propValue.toString());
|
headers.put(propName, propValue.toString());
|
||||||
}
|
}
|
||||||
|
} catch (MQException e) {
|
||||||
|
LOG.warn("Failed to extract MQ property: {}", propName, e);
|
||||||
}
|
}
|
||||||
} catch (JMSException e) {
|
|
||||||
LOG.warn("Failed to extract JMS properties", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void applyMessageProperties(MQMessage message, Map<String, String> properties) {
|
||||||
* Get Queue object from queue name.
|
if (properties == null) {
|
||||||
*/
|
return;
|
||||||
private javax.jms.Queue getQueue(String queueName) {
|
}
|
||||||
return jmsContext.createQueue(queueName);
|
|
||||||
|
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
String value = entry.getValue();
|
||||||
|
try {
|
||||||
|
if (ImqRequest.PROP_JMS_CORRELATION_ID.equals(key)) {
|
||||||
|
message.correlationId = toMqId(value);
|
||||||
|
message.setStringProperty(key, value);
|
||||||
|
} else if (ImqRequest.PROP_JMS_MESSAGE_ID.equals(key)) {
|
||||||
|
message.messageId = toMqId(value);
|
||||||
|
} else if (ImqRequest.PROP_JMS_TYPE.equals(key)) {
|
||||||
|
message.setStringProperty(ImqRequest.PROP_JMS_TYPE, value);
|
||||||
|
} else {
|
||||||
|
message.setStringProperty(key, value);
|
||||||
|
}
|
||||||
|
} catch (MQException e) {
|
||||||
|
LOG.warn("Failed to set property: {}", key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStringProperty(MQMessage message, String property, String defaultValue) {
|
||||||
|
try {
|
||||||
|
String value = message.getStringProperty(property);
|
||||||
|
return value != null ? value : defaultValue;
|
||||||
|
} catch (MQException e) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MQQueue openQueue(String queueName, int openOptions) throws MQException {
|
||||||
|
ensureConnected();
|
||||||
|
return mqQueueManager.accessQueue(queueName, openOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureConnected() {
|
||||||
|
if (mqQueueManager == null || !mqQueueManager.isConnected()) {
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeQueue(MQQueue queue, String queueName) {
|
||||||
|
if (queue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
queue.close();
|
||||||
|
} catch (MQException e) {
|
||||||
|
LOG.warn("Failed to close IBM MQ queue: {}", queueName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int toWaitInterval(long timeoutMs) {
|
||||||
|
if (timeoutMs <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return timeoutMs > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) timeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Charset charsetFor(int ccsid, Charset defaultCharset) {
|
||||||
|
return switch (ccsid) {
|
||||||
|
case 870 -> EBCDIC_870;
|
||||||
|
case 1208 -> UTF_8;
|
||||||
|
default -> defaultCharset;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] toMqId(String value) {
|
||||||
|
if (value == null || value.isBlank()) {
|
||||||
|
return CMQC.MQMI_NONE.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.regionMatches(true, 0, JMS_ID_PREFIX, 0, JMS_ID_PREFIX.length())) {
|
||||||
|
String hex = value.substring(JMS_ID_PREFIX.length());
|
||||||
|
if (hex.length() == CMQC.MQ_MSG_ID_LENGTH * 2 && hex.matches("[0-9A-Fa-f]+")) {
|
||||||
|
return hexToBytes(hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] id = CMQC.MQMI_NONE.clone();
|
||||||
|
byte[] source = value.getBytes(UTF_8);
|
||||||
|
System.arraycopy(source, 0, id, 0, Math.min(source.length, id.length));
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toJmsId(byte[] value) {
|
||||||
|
if (value == null || value.length == 0 || Arrays.equals(value, CMQC.MQMI_NONE)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder(JMS_ID_PREFIX);
|
||||||
|
for (byte b : value) {
|
||||||
|
builder.append(String.format(Locale.ROOT, "%02X", b));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] hexToBytes(String hex) {
|
||||||
|
byte[] data = new byte[hex.length() / 2];
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
int index = i * 2;
|
||||||
|
data[i] = (byte) Integer.parseInt(hex.substring(index, index + 2), 16);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleepQuietly(long millis) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new MessagingDestinationException("Interrupted while waiting for IBM MQ message", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (jmsContext != null) {
|
if (mqQueueManager != null && mqQueueManager.isConnected()) {
|
||||||
try {
|
try {
|
||||||
jmsContext.close();
|
mqQueueManager.disconnect();
|
||||||
LOG.info("Closed connection to IBM MQ: {}", queueManager);
|
LOG.info("Closed connection to IBM MQ: {}", queueManager);
|
||||||
} catch (Exception e) {
|
} catch (MQException e) {
|
||||||
LOG.error("Failed to close IBM MQ connection", e);
|
LOG.error("Failed to close IBM MQ connection", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -477,7 +594,7 @@ public class IbmMqConnector implements Connector {
|
|||||||
throw new IllegalStateException("Keystore not found: " + keystorePath);
|
throw new IllegalStateException("Keystore not found: " + keystorePath);
|
||||||
}
|
}
|
||||||
keyStore.load(ksStream, keystorePassword.toCharArray());
|
keyStore.load(ksStream, keystorePassword.toCharArray());
|
||||||
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
kmf.init(keyStore, keystorePassword.toCharArray());
|
kmf.init(keyStore, keystorePassword.toCharArray());
|
||||||
|
|
||||||
@ -499,4 +616,34 @@ public class IbmMqConnector implements Connector {
|
|||||||
|
|
||||||
return sslContext.getSocketFactory();
|
return sslContext.getSocketFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record Selector(Map<String, String> expectedValues) {
|
||||||
|
|
||||||
|
private static Selector parse(String selector) {
|
||||||
|
if (selector == null || selector.isBlank()) {
|
||||||
|
return new Selector(Map.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> predicates = new HashMap<>();
|
||||||
|
for (String predicate : selector.split("(?i)\\s+AND\\s+")) {
|
||||||
|
Matcher matcher = SELECTOR_EQUALS_PATTERN.matcher(predicate);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported IBM MQ selector expression. Supported format: property = 'value' joined by AND. Selector: "
|
||||||
|
+ selector);
|
||||||
|
}
|
||||||
|
predicates.put(matcher.group(1), matcher.group(2).replace("''", "'"));
|
||||||
|
}
|
||||||
|
return new Selector(predicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matches(Map<String, String> headers) {
|
||||||
|
for (Map.Entry<String, String> expectedValue : expectedValues.entrySet()) {
|
||||||
|
if (!expectedValue.getValue().equals(headers.get(expectedValue.getKey()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,348 +1,369 @@
|
|||||||
package cz.moneta.test.harness.connectors.messaging;
|
package cz.moneta.test.harness.connectors.messaging;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import cz.moneta.test.harness.connectors.messaging.kafkautils.CustomKafkaAvroDeserializer;
|
||||||
import java.time.Duration;
|
import cz.moneta.test.harness.connectors.messaging.kafkautils.JsonToAvroConverter;
|
||||||
import java.util.ArrayList;
|
import cz.moneta.test.harness.support.messaging.exception.MessagingConnectionException;
|
||||||
import java.util.Collections;
|
import cz.moneta.test.harness.support.messaging.exception.MessagingDestinationException;
|
||||||
import java.util.HashMap;
|
import cz.moneta.test.harness.support.messaging.exception.MessagingSchemaException;
|
||||||
import java.util.List;
|
import cz.moneta.test.harness.support.messaging.exception.MessagingTimeoutException;
|
||||||
import java.util.Map;
|
import cz.moneta.test.harness.support.messaging.kafka.MessageContentType;
|
||||||
import java.util.Properties;
|
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;
|
||||||
import java.util.concurrent.ExecutionException;
|
import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
|
||||||
import java.util.function.Predicate;
|
import io.confluent.kafka.schemaregistry.client.SchemaRegistryClientConfig;
|
||||||
|
import io.confluent.kafka.serializers.AbstractKafkaSchemaSerDeConfig;
|
||||||
import org.apache.avro.generic.GenericRecord;
|
import io.confluent.kafka.serializers.KafkaAvroSerializer;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
import org.apache.avro.generic.GenericRecord;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||||
import org.apache.kafka.clients.consumer.ConsumerRecords;
|
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||||
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
import org.apache.kafka.clients.consumer.ConsumerRecords;
|
||||||
import org.apache.kafka.clients.producer.KafkaProducer;
|
import org.apache.kafka.clients.consumer.KafkaConsumer;
|
||||||
import org.apache.kafka.clients.producer.ProducerConfig;
|
import org.apache.kafka.clients.producer.KafkaProducer;
|
||||||
import org.apache.kafka.clients.producer.ProducerRecord;
|
import org.apache.kafka.clients.producer.ProducerConfig;
|
||||||
import org.apache.kafka.common.TopicPartition;
|
import org.apache.kafka.clients.producer.ProducerRecord;
|
||||||
import org.apache.kafka.common.header.Headers;
|
import org.apache.kafka.common.TopicPartition;
|
||||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
import org.apache.kafka.common.header.Headers;
|
||||||
import org.apache.kafka.common.serialization.StringSerializer;
|
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||||
import org.slf4j.Logger;
|
import org.apache.kafka.common.serialization.StringSerializer;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingConnectionException;
|
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingDestinationException;
|
import java.nio.charset.StandardCharsets;
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingSchemaException;
|
import java.time.Duration;
|
||||||
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
import java.util.*;
|
||||||
import cz.moneta.test.harness.support.messaging.kafka.MessageContentType;
|
import java.util.concurrent.ExecutionException;
|
||||||
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;
|
import java.util.function.Predicate;
|
||||||
import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
|
|
||||||
import io.confluent.kafka.serializers.KafkaAvroDeserializer;
|
/**
|
||||||
import io.confluent.kafka.serializers.KafkaAvroSerializer;
|
* Kafka connector for sending and receiving messages.
|
||||||
|
* Supports Avro serialization with Confluent Schema Registry.
|
||||||
/**
|
* <p>
|
||||||
* Kafka connector for sending and receiving messages.
|
* Uses manual partition assignment (no consumer group) for test isolation.
|
||||||
* Supports Avro serialization with Confluent Schema Registry.
|
* Each receive operation creates a new consumer to avoid offset sharing.
|
||||||
* <p>
|
*/
|
||||||
* Uses manual partition assignment (no consumer group) for test isolation.
|
public class KafkaConnector implements cz.moneta.test.harness.connectors.Connector {
|
||||||
* Each receive operation creates a new consumer to avoid offset sharing.
|
|
||||||
*/
|
private static final Logger LOG = LogManager.getLogger(KafkaConnector.class);
|
||||||
public class KafkaConnector implements cz.moneta.test.harness.connectors.Connector {
|
|
||||||
|
private final Properties producerConfig;
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(KafkaConnector.class);
|
private final Properties consumerConfig;
|
||||||
|
private final String schemaRegistryUrl;
|
||||||
private final Properties producerConfig;
|
private final CachedSchemaRegistryClient schemaRegistryClient;
|
||||||
private final Properties consumerConfig;
|
private KafkaProducer<String, GenericRecord> producer;
|
||||||
private final String schemaRegistryUrl;
|
|
||||||
private final CachedSchemaRegistryClient schemaRegistryClient;
|
/**
|
||||||
private KafkaProducer<String, GenericRecord> producer;
|
* Creates a new KafkaConnector.
|
||||||
|
*
|
||||||
/**
|
* @param bootstrapServers Kafka bootstrap servers
|
||||||
* Creates a new KafkaConnector.
|
* @param apiKey Kafka API key
|
||||||
*
|
* @param apiSecret Kafka API secret
|
||||||
* @param bootstrapServers Kafka bootstrap servers
|
* @param schemaRegistryUrl Schema Registry URL
|
||||||
* @param apiKey Kafka API key
|
* @param schemaRegistryApiKey Schema Registry API key
|
||||||
* @param apiSecret Kafka API secret
|
* @param schemaRegistryApiSecret Schema Registry API secret
|
||||||
* @param schemaRegistryUrl Schema Registry URL
|
*/
|
||||||
* @param schemaRegistryApiKey Schema Registry API key
|
public KafkaConnector(String bootstrapServers,
|
||||||
* @param schemaRegistryApiSecret Schema Registry API secret
|
String apiKey,
|
||||||
*/
|
String apiSecret,
|
||||||
public KafkaConnector(String bootstrapServers,
|
String schemaRegistryUrl,
|
||||||
String apiKey,
|
String schemaRegistryApiKey,
|
||||||
String apiSecret,
|
String schemaRegistryApiSecret) {
|
||||||
String schemaRegistryUrl,
|
this.schemaRegistryUrl = schemaRegistryUrl;
|
||||||
String schemaRegistryApiKey,
|
|
||||||
String schemaRegistryApiSecret) {
|
HashMap<String, String> schemaRegistryProps = new HashMap<>();
|
||||||
this.schemaRegistryUrl = schemaRegistryUrl;
|
schemaRegistryProps.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||||
this.schemaRegistryClient = new CachedSchemaRegistryClient(
|
schemaRegistryProps.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||||
Collections.singletonList(schemaRegistryUrl), 100, new HashMap<>());
|
this.schemaRegistryClient = new CachedSchemaRegistryClient(
|
||||||
|
Collections.singletonList(schemaRegistryUrl), 100, schemaRegistryProps);
|
||||||
this.producerConfig = createProducerConfig(bootstrapServers, apiKey, apiSecret);
|
|
||||||
this.consumerConfig = createConsumerConfig(bootstrapServers, schemaRegistryApiKey, schemaRegistryApiSecret);
|
this.producerConfig = createProducerConfig(bootstrapServers, apiKey, apiSecret, schemaRegistryApiKey, schemaRegistryApiSecret);
|
||||||
}
|
this.consumerConfig = createConsumerConfig(bootstrapServers, apiKey, apiSecret, schemaRegistryApiKey, schemaRegistryApiSecret);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Creates producer configuration.
|
/**
|
||||||
*/
|
* Creates producer configuration.
|
||||||
private Properties createProducerConfig(String bootstrapServers, String apiKey, String apiSecret) {
|
*/
|
||||||
Properties config = new Properties();
|
private Properties createProducerConfig(String bootstrapServers, String apiKey, String apiSecret,
|
||||||
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
String schemaRegistryApiKey, String schemaRegistryApiSecret) {
|
||||||
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
Properties config = new Properties();
|
||||||
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class);
|
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
||||||
config.put("schema.registry.url", schemaRegistryUrl);
|
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
||||||
config.put(ProducerConfig.ACKS_CONFIG, "all");
|
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class);
|
||||||
config.put(ProducerConfig.LINGER_MS_CONFIG, 1);
|
config.put(ProducerConfig.ACKS_CONFIG, "all");
|
||||||
config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
|
config.put(ProducerConfig.LINGER_MS_CONFIG, 1);
|
||||||
|
config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
|
||||||
// SASL/PLAIN authentication
|
|
||||||
// config.put("security.protocol", "SASL_SSL");
|
config.put(AbstractKafkaSchemaSerDeConfig.AUTO_REGISTER_SCHEMAS, false);
|
||||||
// config.put("sasl.mechanism", "PLAIN");
|
config.put(AbstractKafkaSchemaSerDeConfig.USE_LATEST_VERSION, true);
|
||||||
// config.put("sasl.jaas.config",
|
config.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
|
||||||
// "org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
config.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||||
// "username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
config.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||||
|
|
||||||
// SSL configuration
|
// SASL/PLAIN authentication
|
||||||
// config.put("ssl.endpoint.identification.algorithm", "https");
|
config.put("security.protocol", "SASL_SSL");
|
||||||
|
config.put("sasl.mechanism", "PLAIN");
|
||||||
return config;
|
config.put("sasl.jaas.config",
|
||||||
}
|
"org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
||||||
|
"username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
||||||
/**
|
|
||||||
* Creates consumer configuration.
|
// SSL configuration
|
||||||
*/
|
config.put("ssl.endpoint.identification.algorithm", "https");
|
||||||
private Properties createConsumerConfig(String bootstrapServers, String apiKey, String apiSecret) {
|
|
||||||
Properties config = new Properties();
|
return config;
|
||||||
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
}
|
||||||
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
|
||||||
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class);
|
/**
|
||||||
config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "none");
|
* Creates consumer configuration.
|
||||||
config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
|
*/
|
||||||
|
private Properties createConsumerConfig(String bootstrapServers, String apiKey, String apiSecret,
|
||||||
// SASL/PLAIN authentication
|
String schemaRegistryApiKey, String schemaRegistryApiSecret) {
|
||||||
config.put("security.protocol", "SASL_SSL");
|
Properties config = new Properties();
|
||||||
config.put("sasl.mechanism", "PLAIN");
|
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
||||||
config.put("sasl.jaas.config",
|
config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
|
||||||
"org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, CustomKafkaAvroDeserializer.class);
|
||||||
"username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "none");
|
||||||
|
config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
|
||||||
// Schema Registry for deserialization
|
|
||||||
config.put("schema.registry.url", schemaRegistryUrl);
|
|
||||||
config.put("specific.avro.reader", false);
|
// SASL/PLAIN authentication
|
||||||
|
config.put("security.protocol", "SASL_SSL");
|
||||||
// SSL configuration
|
config.put("sasl.mechanism", "PLAIN");
|
||||||
config.put("ssl.endpoint.identification.algorithm", "https");
|
config.put("sasl.jaas.config",
|
||||||
|
"org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
||||||
return config;
|
"username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
||||||
}
|
|
||||||
|
// Schema Registry for deserialization
|
||||||
/**
|
|
||||||
* Sends a message to a Kafka topic.
|
config.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
|
||||||
*/
|
config.put(AbstractKafkaSchemaSerDeConfig.AUTO_REGISTER_SCHEMAS, false);
|
||||||
public void send(String topic, String key, String jsonPayload, Map<String, String> headers) {
|
config.put(AbstractKafkaSchemaSerDeConfig.USE_LATEST_VERSION, true);
|
||||||
try {
|
config.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||||
org.apache.avro.Schema schema = getSchemaForTopic(topic);
|
config.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||||
GenericRecord record = jsonToAvro(jsonPayload, schema);
|
config.put("specific.avro.reader", false);
|
||||||
|
|
||||||
ProducerRecord<String, GenericRecord> producerRecord =
|
// SSL configuration
|
||||||
new ProducerRecord<>(topic, key, record);
|
config.put("ssl.endpoint.identification.algorithm", "https");
|
||||||
|
|
||||||
// Add headers
|
return config;
|
||||||
if (headers != null) {
|
}
|
||||||
Headers kafkaHeaders = producerRecord.headers();
|
|
||||||
headers.forEach((k, v) ->
|
/**
|
||||||
kafkaHeaders.add(k, v.getBytes(StandardCharsets.UTF_8)));
|
* Sends a message to a Kafka topic.
|
||||||
}
|
*/
|
||||||
|
public void send(String topic, String key, String jsonPayload, Map<String, String> headers) {
|
||||||
// Send and wait for confirmation
|
try {
|
||||||
getProducer().send(producerRecord, (metadata, exception) -> {
|
org.apache.avro.Schema schema = getSchemaForTopic(topic);
|
||||||
if (exception != null) {
|
GenericRecord record = jsonToAvro(jsonPayload, schema);
|
||||||
LOG.error("Failed to send message", exception);
|
|
||||||
} else {
|
ProducerRecord<String, GenericRecord> producerRecord =
|
||||||
LOG.debug("Message sent to topic {} partition {} offset {}",
|
new ProducerRecord<>(topic, key, record);
|
||||||
metadata.topic(), metadata.partition(), metadata.offset());
|
|
||||||
}
|
// Add headers
|
||||||
}).get(10, java.util.concurrent.TimeUnit.SECONDS);
|
if (headers != null) {
|
||||||
|
Headers kafkaHeaders = producerRecord.headers();
|
||||||
} catch (ExecutionException e) {
|
headers.forEach((k, v) ->
|
||||||
throw new MessagingConnectionException(
|
kafkaHeaders.add(k, v.getBytes(StandardCharsets.UTF_8)));
|
||||||
"Failed to send message to topic " + topic, e.getCause());
|
}
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
// Send and wait for confirmation
|
||||||
throw new MessagingConnectionException(
|
getProducer().send(producerRecord, (metadata, exception) -> {
|
||||||
"Interrupted while sending message to topic " + topic, e);
|
if (exception != null) {
|
||||||
} catch (MessagingSchemaException e) {
|
LOG.error("Failed to send message", exception);
|
||||||
throw e;
|
} else {
|
||||||
} catch (Exception e) {
|
LOG.debug("Message sent to topic {} partition {} offset {}",
|
||||||
throw new MessagingSchemaException(
|
metadata.topic(), metadata.partition(), metadata.offset());
|
||||||
"Failed to convert JSON to Avro for topic " + topic, e);
|
}
|
||||||
}
|
}).get(10, java.util.concurrent.TimeUnit.SECONDS);
|
||||||
}
|
|
||||||
|
} catch (ExecutionException e) {
|
||||||
/**
|
throw new MessagingConnectionException(
|
||||||
* Receives a message from a Kafka topic matching the filter.
|
"Failed to send message to topic " + topic, e.getCause());
|
||||||
*/
|
} catch (InterruptedException e) {
|
||||||
public List<ReceivedMessage> receive(String topic,
|
Thread.currentThread().interrupt();
|
||||||
Predicate<ReceivedMessage> filter,
|
throw new MessagingConnectionException(
|
||||||
Duration timeout) {
|
"Interrupted while sending message to topic " + topic, e);
|
||||||
KafkaConsumer<String, GenericRecord> consumer = null;
|
} catch (MessagingSchemaException e) {
|
||||||
try {
|
throw e;
|
||||||
consumer = new KafkaConsumer<>(consumerConfig);
|
} catch (Exception e) {
|
||||||
|
throw new MessagingSchemaException(
|
||||||
// Get partitions for the topic
|
"Failed to convert JSON to Avro for topic " + topic, e);
|
||||||
List<TopicPartition> partitions = getPartitionsForTopic(consumer, topic);
|
}
|
||||||
if (partitions.isEmpty()) {
|
}
|
||||||
throw new MessagingDestinationException(
|
|
||||||
"Topic '" + topic + "' does not exist or has no partitions");
|
/**
|
||||||
}
|
* Receives a message from a Kafka topic matching the filter.
|
||||||
|
*/
|
||||||
// Assign partitions and seek to end
|
public List<ReceivedMessage> receive(String topic,
|
||||||
consumer.assign(partitions);
|
Predicate<ReceivedMessage> filter,
|
||||||
// consumer.seekToBeginning(partitions);
|
Duration timeout, boolean startFromBeginning) {
|
||||||
consumer.seekToBeginning(partitions);
|
KafkaConsumer<String, GenericRecord> consumer = null;
|
||||||
|
try {
|
||||||
// Poll loop with exponential backoff
|
consumer = new KafkaConsumer<>(consumerConfig);
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
Duration pollInterval = Duration.ofMillis(100);
|
// Get partitions for the topic
|
||||||
Duration maxPollInterval = Duration.ofSeconds(1);
|
List<TopicPartition> partitions = getPartitionsForTopic(consumer, topic);
|
||||||
|
if (partitions.isEmpty()) {
|
||||||
while (Duration.ofMillis(System.currentTimeMillis() - startTime).compareTo(timeout) < 0) {
|
throw new MessagingDestinationException(
|
||||||
ConsumerRecords<String, GenericRecord> records = consumer.poll(pollInterval);
|
"Topic '" + topic + "' does not exist or has no partitions");
|
||||||
|
}
|
||||||
for (ConsumerRecord<String, GenericRecord> record : records) {
|
|
||||||
ReceivedMessage message = convertToReceivedMessage(record, topic);
|
consumer.assign(partitions);
|
||||||
if (filter.test(message)) {
|
if (startFromBeginning) {
|
||||||
LOG.debug("Found matching message on topic {} partition {} offset {}",
|
consumer.seekToBeginning(partitions);
|
||||||
record.topic(), record.partition(), record.offset());
|
} else {
|
||||||
return Collections.singletonList(message);
|
consumer.seekToEnd(partitions);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Poll loop with exponential backoff
|
||||||
// Exponential backoff
|
long startTime = System.currentTimeMillis();
|
||||||
pollInterval = Duration.ofMillis(
|
Duration pollInterval = Duration.ofMillis(100);
|
||||||
Math.min(pollInterval.toMillis() * 2, maxPollInterval.toMillis()));
|
Duration maxPollInterval = Duration.ofSeconds(1);
|
||||||
}
|
|
||||||
|
while (Duration.ofMillis(System.currentTimeMillis() - startTime).compareTo(timeout) < 0) {
|
||||||
throw new MessagingTimeoutException(
|
ConsumerRecords<String, GenericRecord> records = consumer.poll(pollInterval);
|
||||||
"No message matching filter found on topic '" + topic + "' within " + timeout);
|
|
||||||
|
for (ConsumerRecord<String, GenericRecord> record : records) {
|
||||||
} finally {
|
ReceivedMessage message = convertToReceivedMessage(record, topic);
|
||||||
if (consumer != null) {
|
if (filter.test(message)) {
|
||||||
consumer.close();
|
LOG.debug("Found matching message on topic {} partition {} offset {}",
|
||||||
}
|
record.topic(), record.partition(), record.offset());
|
||||||
}
|
return Collections.singletonList(message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Gets partitions for a topic.
|
// Exponential backoff
|
||||||
*/
|
pollInterval = Duration.ofMillis(
|
||||||
private List<TopicPartition> getPartitionsForTopic(KafkaConsumer<?, ?> consumer, String topic) {
|
Math.min(pollInterval.toMillis() * 2, maxPollInterval.toMillis()));
|
||||||
List<TopicPartition> partitions = new ArrayList<>();
|
}
|
||||||
List<org.apache.kafka.common.PartitionInfo> partitionInfos = consumer.partitionsFor(topic);
|
|
||||||
if (partitionInfos != null) {
|
throw new MessagingTimeoutException(
|
||||||
for (org.apache.kafka.common.PartitionInfo partitionInfo : partitionInfos) {
|
"No message matching filter found on topic '" + topic + "' within " + timeout);
|
||||||
partitions.add(new TopicPartition(topic, partitionInfo.partition()));
|
|
||||||
}
|
} finally {
|
||||||
}
|
if (consumer != null) {
|
||||||
return partitions;
|
consumer.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Saves current offsets for a topic.
|
|
||||||
*/
|
/**
|
||||||
public Map<TopicPartition, Long> saveOffsets(String topic) {
|
* Gets partitions for a topic.
|
||||||
return new HashMap<>();
|
*/
|
||||||
}
|
private List<TopicPartition> getPartitionsForTopic(KafkaConsumer<?, ?> consumer, String topic) {
|
||||||
|
List<TopicPartition> partitions = new ArrayList<>();
|
||||||
/**
|
List<org.apache.kafka.common.PartitionInfo> partitionInfos = consumer.partitionsFor(topic);
|
||||||
* Closes the connector and releases resources.
|
if (partitionInfos != null) {
|
||||||
*/
|
for (org.apache.kafka.common.PartitionInfo partitionInfo : partitionInfos) {
|
||||||
@Override
|
partitions.add(new TopicPartition(topic, partitionInfo.partition()));
|
||||||
public void close() {
|
}
|
||||||
if (producer != null) {
|
}
|
||||||
producer.close();
|
return partitions;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/**
|
* Saves current offsets for a topic.
|
||||||
* Gets or creates the producer (singleton, thread-safe).
|
*/
|
||||||
*/
|
public Map<TopicPartition, Long> saveOffsets(String topic) {
|
||||||
private KafkaProducer<String, GenericRecord> getProducer() {
|
return new HashMap<>();
|
||||||
if (producer == null) {
|
}
|
||||||
synchronized (this) {
|
|
||||||
if (producer == null) {
|
/**
|
||||||
producer = new KafkaProducer<>(producerConfig);
|
* Closes the connector and releases resources.
|
||||||
}
|
*/
|
||||||
}
|
@Override
|
||||||
}
|
public void close() {
|
||||||
return producer;
|
if (producer != null) {
|
||||||
}
|
producer.close();
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Retrieves schema from Schema Registry based on topic name.
|
|
||||||
*/
|
/**
|
||||||
private org.apache.avro.Schema getSchemaForTopic(String topic) {
|
* Gets or creates the producer (singleton, thread-safe).
|
||||||
String subject = topic + "-value";
|
*/
|
||||||
try {
|
private KafkaProducer<String, GenericRecord> getProducer() {
|
||||||
io.confluent.kafka.schemaregistry.client.SchemaMetadata metadata =
|
if (producer == null) {
|
||||||
schemaRegistryClient.getLatestSchemaMetadata(subject);
|
synchronized (this) {
|
||||||
return new org.apache.avro.Schema.Parser().parse(metadata.getSchema());
|
if (producer == null) {
|
||||||
} catch (Exception e) {
|
producer = new KafkaProducer<>(producerConfig);
|
||||||
if (e.getMessage() != null && e.getMessage().contains("404")) {
|
}
|
||||||
throw new MessagingSchemaException(
|
}
|
||||||
"Schema not found for subject '" + subject + "' in Schema Registry. " +
|
}
|
||||||
"Make sure the topic exists and schema is registered.");
|
return producer;
|
||||||
}
|
}
|
||||||
throw new MessagingSchemaException(
|
|
||||||
"Failed to retrieve schema for topic '" + topic + "'", e);
|
/**
|
||||||
}
|
* Retrieves schema from Schema Registry based on topic name.
|
||||||
}
|
*/
|
||||||
|
private org.apache.avro.Schema getSchemaForTopic(String topic) {
|
||||||
/**
|
String subject = topic + "-value";
|
||||||
* Converts JSON string to Avro GenericRecord.
|
try {
|
||||||
*/
|
io.confluent.kafka.schemaregistry.client.SchemaMetadata metadata =
|
||||||
private GenericRecord jsonToAvro(String json, org.apache.avro.Schema schema) {
|
schemaRegistryClient.getLatestSchemaMetadata(subject);
|
||||||
try {
|
return new org.apache.avro.Schema.Parser().parse(metadata.getSchema());
|
||||||
GenericRecord genericRecord = JsonToAvroConverter.processJson(json, schema);
|
} catch (Exception e) {
|
||||||
return genericRecord;
|
if (e.getMessage() != null && e.getMessage().contains("404")) {
|
||||||
} catch (Exception e) {
|
throw new MessagingSchemaException(
|
||||||
throw new MessagingSchemaException("Failed to convert JSON to Avro: " + e.getMessage(), e);
|
"Schema not found for subject '" + subject + "' in Schema Registry. " +
|
||||||
}
|
"Make sure the topic exists and schema is registered.");
|
||||||
}
|
}
|
||||||
|
throw new MessagingSchemaException(
|
||||||
/**
|
"Failed to retrieve schema for topic '" + topic + "'", e);
|
||||||
* Converts Kafka ConsumerRecord to ReceivedMessage.
|
}
|
||||||
*/
|
}
|
||||||
private ReceivedMessage convertToReceivedMessage(ConsumerRecord<String, GenericRecord> record, String topic) {
|
|
||||||
try {
|
/**
|
||||||
String jsonBody = avroToJson(record.value());
|
* Converts JSON string to Avro GenericRecord.
|
||||||
|
*/
|
||||||
Map<String, String> headers = new HashMap<>();
|
private GenericRecord jsonToAvro(String json, org.apache.avro.Schema schema) {
|
||||||
Headers kafkaHeaders = record.headers();
|
try {
|
||||||
for (org.apache.kafka.common.header.Header header : kafkaHeaders) {
|
GenericRecord genericRecord = JsonToAvroConverter.processJson(json, schema);
|
||||||
if (header.value() != null) {
|
return genericRecord;
|
||||||
headers.put(header.key(), new String(header.value(), StandardCharsets.UTF_8));
|
} catch (Exception e) {
|
||||||
}
|
throw new MessagingSchemaException("Failed to convert JSON to Avro: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ReceivedMessage.builder()
|
|
||||||
.body(jsonBody)
|
/**
|
||||||
.contentType(MessageContentType.JSON)
|
* Converts Kafka ConsumerRecord to ReceivedMessage.
|
||||||
.headers(headers)
|
*/
|
||||||
.timestamp(record.timestamp())
|
private ReceivedMessage convertToReceivedMessage(ConsumerRecord<String, GenericRecord> record, String topic) {
|
||||||
.source(topic)
|
try {
|
||||||
.key(record.key())
|
String jsonBody;
|
||||||
.build();
|
if (null != record.value()) {
|
||||||
|
jsonBody = avroToJson(record.value());
|
||||||
} catch (Exception e) {
|
} else {
|
||||||
LOG.error("Failed to convert Avro record to ReceivedMessage", e);
|
jsonBody = "";
|
||||||
throw new RuntimeException("Failed to convert message", e);
|
}
|
||||||
}
|
Map<String, String> headers = new HashMap<>();
|
||||||
}
|
Headers kafkaHeaders = record.headers();
|
||||||
|
for (org.apache.kafka.common.header.Header header : kafkaHeaders) {
|
||||||
/**
|
if (header.value() != null) {
|
||||||
* Converts Avro GenericRecord to JSON string.
|
headers.put(header.key(), new String(header.value(), StandardCharsets.UTF_8));
|
||||||
*/
|
}
|
||||||
private String avroToJson(GenericRecord record) {
|
}
|
||||||
try {
|
|
||||||
return record.toString();
|
return ReceivedMessage.builder()
|
||||||
} catch (Exception e) {
|
.body(jsonBody)
|
||||||
throw new RuntimeException("Failed to convert Avro to JSON: " + e.getMessage(), e);
|
.contentType(MessageContentType.JSON)
|
||||||
}
|
.headers(headers)
|
||||||
}
|
.timestamp(record.timestamp())
|
||||||
}
|
.source(topic)
|
||||||
|
.key(record.key())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to convert Avro record to ReceivedMessage", e);
|
||||||
|
throw new RuntimeException("Failed to convert message", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts Avro GenericRecord to JSON string.
|
||||||
|
*/
|
||||||
|
private String avroToJson(GenericRecord record) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
return record.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to convert Avro to JSON: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,54 @@
|
|||||||
|
package cz.moneta.test.harness.connectors.messaging.kafkautils;
|
||||||
|
|
||||||
|
import cz.moneta.test.harness.connectors.messaging.KafkaConnector;
|
||||||
|
import io.confluent.kafka.serializers.KafkaAvroDeserializer;
|
||||||
|
import org.apache.avro.Schema;
|
||||||
|
import org.apache.kafka.common.errors.SerializationException;
|
||||||
|
import org.apache.kafka.common.header.Headers;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
public class CustomKafkaAvroDeserializer extends KafkaAvroDeserializer {
|
||||||
|
|
||||||
|
private static final Logger LOG = LogManager.getLogger(KafkaConnector.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object deserialize(String topic, byte[] bytes) {
|
||||||
|
try {
|
||||||
|
return super.deserialize(topic, bytes);
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
LOG.error("Message deserialization by avro schema failed", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object deserialize(String topic, Headers headers, byte[] bytes) {
|
||||||
|
try {
|
||||||
|
return super.deserialize(topic, headers, bytes);
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
LOG.error("Message deserialization by avro schema failed", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object deserialize(String topic, byte[] bytes, Schema readerSchema) {
|
||||||
|
try {
|
||||||
|
return super.deserialize(topic, bytes, readerSchema);
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
LOG.error("Message deserialization by avro schema failed", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object deserialize(String topic, Headers headers, byte[] bytes, Schema readerSchema) {
|
||||||
|
try {
|
||||||
|
return super.deserialize(topic, headers, bytes, readerSchema);
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
LOG.error("Message deserialization by avro schema failed", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,115 +1,109 @@
|
|||||||
package cz.moneta.test.harness.connectors.messaging;
|
package cz.moneta.test.harness.connectors.messaging.kafkautils;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import org.apache.avro.Schema;
|
import org.apache.avro.Schema;
|
||||||
import org.apache.avro.generic.GenericData;
|
import org.apache.avro.generic.GenericData;
|
||||||
import org.apache.avro.generic.GenericDatumReader;
|
import org.apache.avro.generic.GenericRecord;
|
||||||
import org.apache.avro.generic.GenericDatumWriter;
|
|
||||||
import org.apache.avro.generic.GenericRecord;
|
import java.util.ArrayList;
|
||||||
import org.apache.avro.io.DatumReader;
|
import java.util.List;
|
||||||
import org.apache.avro.io.DatumWriter;
|
|
||||||
import org.apache.avro.io.Decoder;
|
public class JsonToAvroConverter {
|
||||||
import org.apache.avro.io.JsonDecoder;
|
|
||||||
|
public static GenericRecord processJson(String json, Schema schema) throws IllegalArgumentException, JsonSchemaException {
|
||||||
import java.util.ArrayList;
|
GenericRecord result = (GenericRecord) jsonElementToAvro(JsonParser.parseString(json), schema);
|
||||||
import java.util.List;
|
return result;
|
||||||
|
}
|
||||||
public class JsonToAvroConverter {
|
|
||||||
|
private static Object jsonElementToAvro(JsonElement element, Schema schema) throws JsonSchemaException {
|
||||||
protected static GenericRecord processJson(String json, Schema schema) throws IllegalArgumentException, JsonSchemaException {
|
boolean schemaIsNullable = isNullable(schema);
|
||||||
GenericRecord result = (GenericRecord) jsonElementToAvro(JsonParser.parseString(json), schema);
|
if (schemaIsNullable) {
|
||||||
return result;
|
schema = typeFromNullable(schema);
|
||||||
}
|
}
|
||||||
|
if (element == null || element.isJsonNull()) {
|
||||||
private static Object jsonElementToAvro(JsonElement element, Schema schema) throws JsonSchemaException {
|
if (!schemaIsNullable) {
|
||||||
boolean schemaIsNullable = isNullable(schema);
|
throw new JsonSchemaException("The element is not nullable in Avro schema.");
|
||||||
if (schemaIsNullable) {
|
}
|
||||||
schema = typeFromNullable(schema);
|
return null;
|
||||||
}
|
} else if (element.isJsonObject()) {
|
||||||
if (element == null || element.isJsonNull()) {
|
if (schema.getType() != Schema.Type.RECORD) {
|
||||||
if (!schemaIsNullable) {
|
throw new JsonSchemaException(
|
||||||
throw new JsonSchemaException("The element is not nullable in Avro schema.");
|
String.format("The element `%s` doesn't match Avro type RECORD", element));
|
||||||
}
|
}
|
||||||
return null;
|
return jsonObjectToAvro(element.getAsJsonObject(), schema);
|
||||||
} else if (element.isJsonObject()) {
|
} else if (element.isJsonArray()) {
|
||||||
if (schema.getType() != Schema.Type.RECORD) {
|
if (schema.getType() != Schema.Type.ARRAY) {
|
||||||
throw new JsonSchemaException(
|
throw new JsonSchemaException(
|
||||||
String.format("The element `%s` doesn't match Avro type RECORD", element));
|
String.format("The element `%s` doesn't match Avro type ARRAY", element));
|
||||||
}
|
}
|
||||||
return jsonObjectToAvro(element.getAsJsonObject(), schema);
|
JsonArray jsonArray = element.getAsJsonArray();
|
||||||
} else if (element.isJsonArray()) {
|
List<Object> avroArray = new ArrayList<>(jsonArray.size());
|
||||||
if (schema.getType() != Schema.Type.ARRAY) {
|
for (JsonElement e : element.getAsJsonArray()) {
|
||||||
throw new JsonSchemaException(
|
avroArray.add(jsonElementToAvro(e, schema.getElementType()));
|
||||||
String.format("The element `%s` doesn't match Avro type ARRAY", element));
|
}
|
||||||
}
|
return avroArray;
|
||||||
JsonArray jsonArray = element.getAsJsonArray();
|
} else if (element.isJsonPrimitive()) {
|
||||||
List<Object> avroArray = new ArrayList<>(jsonArray.size());
|
return jsonPrimitiveToAvro(element.getAsJsonPrimitive(), schema);
|
||||||
for (JsonElement e : element.getAsJsonArray()) {
|
} else {
|
||||||
avroArray.add(jsonElementToAvro(e, schema.getElementType()));
|
throw new JsonSchemaException(
|
||||||
}
|
String.format(
|
||||||
return avroArray;
|
"The Json element `%s` is of an unknown class %s", element, element.getClass()));
|
||||||
} else if (element.isJsonPrimitive()) {
|
}
|
||||||
return jsonPrimitiveToAvro(element.getAsJsonPrimitive(), schema);
|
}
|
||||||
} else {
|
|
||||||
throw new JsonSchemaException(
|
private static GenericRecord jsonObjectToAvro(JsonObject jsonObject, Schema schema) throws JsonSchemaException {
|
||||||
String.format(
|
GenericRecord avroRecord = new GenericData.Record(schema);
|
||||||
"The Json element `%s` is of an unknown class %s", element, element.getClass()));
|
|
||||||
}
|
for (Schema.Field field : schema.getFields()) {
|
||||||
}
|
avroRecord.put(field.name(), jsonElementToAvro(jsonObject.get(field.name()), field.schema()));
|
||||||
|
}
|
||||||
private static GenericRecord jsonObjectToAvro(JsonObject jsonObject, Schema schema) throws JsonSchemaException {
|
return avroRecord;
|
||||||
GenericRecord avroRecord = new GenericData.Record(schema);
|
}
|
||||||
|
|
||||||
for (Schema.Field field : schema.getFields()) {
|
private static boolean isNullable(Schema type) {
|
||||||
avroRecord.put(field.name(), jsonElementToAvro(jsonObject.get(field.name()), field.schema()));
|
return type.getType() == Schema.Type.NULL
|
||||||
}
|
|| type.getType() == Schema.Type.UNION
|
||||||
return avroRecord;
|
&& type.getTypes().stream().anyMatch(JsonToAvroConverter::isNullable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isNullable(Schema type) {
|
private static Schema typeFromNullable(Schema type) {
|
||||||
return type.getType() == Schema.Type.NULL
|
if (type.getType() == Schema.Type.UNION) {
|
||||||
|| type.getType() == Schema.Type.UNION
|
return typeFromNullable(
|
||||||
&& type.getTypes().stream().anyMatch(JsonToAvroConverter::isNullable);
|
type.getTypes().stream()
|
||||||
}
|
.filter(t -> t.getType() != Schema.Type.NULL)
|
||||||
|
.findFirst()
|
||||||
private static Schema typeFromNullable(Schema type) {
|
.orElseThrow(
|
||||||
if (type.getType() == Schema.Type.UNION) {
|
() ->
|
||||||
return typeFromNullable(
|
new IllegalStateException(
|
||||||
type.getTypes().stream()
|
String.format("Type `%s` should have a non null subtype", type))));
|
||||||
.filter(t -> t.getType() != Schema.Type.NULL)
|
}
|
||||||
.findFirst()
|
return type;
|
||||||
.orElseThrow(
|
}
|
||||||
() ->
|
|
||||||
new IllegalStateException(
|
private static Object jsonPrimitiveToAvro(JsonPrimitive primitive, Schema schema) {
|
||||||
String.format("Type `%s` should have a non null subtype", type))));
|
switch (schema.getType()) {
|
||||||
}
|
case NULL:
|
||||||
return type;
|
return null;
|
||||||
}
|
case STRING:
|
||||||
|
return primitive.getAsString();
|
||||||
private static Object jsonPrimitiveToAvro(JsonPrimitive primitive, Schema schema){
|
case BOOLEAN:
|
||||||
switch (schema.getType()) {
|
return primitive.getAsBoolean();
|
||||||
case NULL:
|
case INT:
|
||||||
return null;
|
return primitive.getAsInt();
|
||||||
case STRING:
|
case LONG:
|
||||||
return primitive.getAsString();
|
return primitive.getAsLong();
|
||||||
case BOOLEAN:
|
case FLOAT:
|
||||||
return primitive.getAsBoolean();
|
return primitive.getAsFloat();
|
||||||
case INT:
|
case DOUBLE:
|
||||||
return primitive.getAsInt();
|
return primitive.getAsDouble();
|
||||||
case LONG:
|
default:
|
||||||
return primitive.getAsLong();
|
return primitive.getAsString();
|
||||||
case FLOAT:
|
}
|
||||||
return primitive.getAsFloat();
|
}
|
||||||
case DOUBLE:
|
|
||||||
return primitive.getAsDouble();
|
private static class JsonSchemaException extends Exception {
|
||||||
default:
|
JsonSchemaException(String message) {
|
||||||
return primitive.getAsString();
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static class JsonSchemaException extends Exception {
|
|
||||||
JsonSchemaException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,338 +1,338 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile;
|
package cz.moneta.test.harness.connectors.mobile;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.Connector;
|
import cz.moneta.test.harness.connectors.Connector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
||||||
import cz.moneta.test.harness.support.web.TextContainer;
|
import cz.moneta.test.harness.support.web.TextContainer;
|
||||||
import cz.moneta.test.harness.support.web.Until;
|
import cz.moneta.test.harness.support.web.Until;
|
||||||
import io.appium.java_client.AppiumDriver;
|
import io.appium.java_client.AppiumDriver;
|
||||||
import io.appium.java_client.MobileBy;
|
import io.appium.java_client.MobileBy;
|
||||||
import io.appium.java_client.remote.MobileCapabilityType;
|
import io.appium.java_client.remote.MobileCapabilityType;
|
||||||
import io.appium.java_client.touch.offset.PointOption;
|
import io.appium.java_client.touch.offset.PointOption;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.remote.CapabilityType;
|
import org.openqa.selenium.remote.CapabilityType;
|
||||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
import org.openqa.selenium.support.ui.ExpectedCondition;
|
import org.openqa.selenium.support.ui.ExpectedCondition;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.FluentWait;
|
import org.openqa.selenium.support.ui.FluentWait;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static io.appium.java_client.touch.offset.PointOption.point;
|
import static io.appium.java_client.touch.offset.PointOption.point;
|
||||||
import static java.time.Duration.ofMillis;
|
import static java.time.Duration.ofMillis;
|
||||||
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
|
import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
|
||||||
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
|
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
|
||||||
|
|
||||||
public abstract class AppiumMobileConnector<A extends AppiumDriver> implements Connector {
|
public abstract class AppiumMobileConnector<A extends AppiumDriver> implements Connector {
|
||||||
private static final Logger logger = LogManager.getLogger(AppiumMobileConnector.class);
|
private static final Logger logger = LogManager.getLogger(AppiumMobileConnector.class);
|
||||||
private static final int LAZY_ELEMENT_RENDER_TIMEOUT = 5;
|
private static final int LAZY_ELEMENT_RENDER_TIMEOUT = 5;
|
||||||
protected String deviceName;
|
protected String deviceName;
|
||||||
protected String udid;
|
protected String udid;
|
||||||
protected String bundleId;
|
protected String bundleId;
|
||||||
protected String appBinaryPath;
|
protected String appBinaryPath;
|
||||||
protected String host;
|
protected String host;
|
||||||
protected final StoreAccessor store;
|
protected final StoreAccessor store;
|
||||||
private final Function<String, By> defaultLookup;
|
private final Function<String, By> defaultLookup;
|
||||||
|
|
||||||
protected abstract A getDriver();
|
protected abstract A getDriver();
|
||||||
|
|
||||||
public abstract void captureVideo(String snapshotFileName);
|
public abstract void captureVideo(String snapshotFileName);
|
||||||
|
|
||||||
public abstract void resetApp();
|
public abstract void resetApp();
|
||||||
|
|
||||||
public abstract void close();
|
public abstract void close();
|
||||||
|
|
||||||
public AppiumMobileConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String appBinaryPath) {
|
public AppiumMobileConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String appBinaryPath) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.defaultLookup = defaultLookup;
|
this.defaultLookup = defaultLookup;
|
||||||
this.appBinaryPath = appBinaryPath;
|
this.appBinaryPath = appBinaryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDeviceName() {
|
public String getDeviceName() {
|
||||||
return deviceName;
|
return deviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setDesiredCapabilities(Platform platformName, String deviceName, String udid, String automationName, DesiredCapabilities capabilities) {
|
protected void setDesiredCapabilities(Platform platformName, String deviceName, String udid, String automationName, DesiredCapabilities capabilities) {
|
||||||
capabilities.setCapability(CapabilityType.PLATFORM_NAME, platformName);
|
capabilities.setCapability(CapabilityType.PLATFORM_NAME, platformName);
|
||||||
// capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
|
// capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName);
|
||||||
// capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 150);
|
// capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 150);
|
||||||
// capabilities.setCapability(MobileCapabilityType.NO_RESET, true);
|
// capabilities.setCapability(MobileCapabilityType.NO_RESET, true);
|
||||||
// capabilities.setCapability(MobileCapabilityType.UDID, udid);
|
// capabilities.setCapability(MobileCapabilityType.UDID, udid);
|
||||||
// capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, automationName);
|
// capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, automationName);
|
||||||
// capabilities.setCapability(MobileCapabilityType.APP, appBinaryPath);
|
// capabilities.setCapability(MobileCapabilityType.APP, appBinaryPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Optional<A> launchExistingApp(A driver) {
|
protected Optional<A> launchExistingApp(A driver) {
|
||||||
// return Optional.of(driver)
|
// return Optional.of(driver)
|
||||||
// .map(app -> {
|
// .map(app -> {
|
||||||
// driver.launchApp();
|
// driver.launchApp();
|
||||||
// return driver;
|
// return driver;
|
||||||
// });
|
// });
|
||||||
return Optional.of(driver);
|
return Optional.of(driver);
|
||||||
// TODO: actually not supported
|
// TODO: actually not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
private void installApp(String path, A driver) {
|
private void installApp(String path, A driver) {
|
||||||
// Optional.of(bundleId)
|
// Optional.of(bundleId)
|
||||||
// .filter(app -> !driver.isAppInstalled(app))
|
// .filter(app -> !driver.isAppInstalled(app))
|
||||||
// .map(installApp -> {
|
// .map(installApp -> {
|
||||||
// logger.info("App was not found on the device " + deviceName + " and will be installed");
|
// logger.info("App was not found on the device " + deviceName + " and will be installed");
|
||||||
// try {
|
// try {
|
||||||
// driver.installApp(path);
|
// driver.installApp(path);
|
||||||
// } catch (WebDriverException e) {
|
// } catch (WebDriverException e) {
|
||||||
// throw new HarnessException("The application at " + path + " does not exist or is not accessible. " +
|
// throw new HarnessException("The application at " + path + " does not exist or is not accessible. " +
|
||||||
// "Please, check if the path is correct");
|
// "Please, check if the path is correct");
|
||||||
// }
|
// }
|
||||||
// return driver;
|
// return driver;
|
||||||
// });
|
// });
|
||||||
// TODO: actually not supported
|
// TODO: actually not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void mobileLanguage(DesiredCapabilities capabilities, String ENPlatformLocale, String CZPlatformLocale) {
|
protected void mobileLanguage(DesiredCapabilities capabilities, String ENPlatformLocale, String CZPlatformLocale) {
|
||||||
String languageKey = store.getConfig("appium.language", "cs");
|
String languageKey = store.getConfig("appium.language", "cs");
|
||||||
switch (languageKey) {
|
switch (languageKey) {
|
||||||
case "en":
|
case "en":
|
||||||
capabilities.setCapability(MobileCapabilityType.LANGUAGE, "en");
|
capabilities.setCapability(MobileCapabilityType.LANGUAGE, "en");
|
||||||
capabilities.setCapability(MobileCapabilityType.LOCALE, ENPlatformLocale);
|
capabilities.setCapability(MobileCapabilityType.LOCALE, ENPlatformLocale);
|
||||||
break;
|
break;
|
||||||
case "cs":
|
case "cs":
|
||||||
capabilities.setCapability(MobileCapabilityType.LANGUAGE, "cs");
|
capabilities.setCapability(MobileCapabilityType.LANGUAGE, "cs");
|
||||||
capabilities.setCapability(MobileCapabilityType.LOCALE, CZPlatformLocale);
|
capabilities.setCapability(MobileCapabilityType.LOCALE, CZPlatformLocale);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HarnessException("Please, define the appium.language parameter. Available language is cs or en");
|
throw new HarnessException("Please, define the appium.language parameter. Available language is cs or en");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Boolean checkVideoRecord() {
|
protected Boolean checkVideoRecord() {
|
||||||
return Boolean.parseBoolean(store.getConfig("appium.video.record", "false"));
|
return Boolean.parseBoolean(store.getConfig("appium.video.record", "false"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void click(String path, MobileLookup mobileLookup) {
|
public void click(String path, MobileLookup mobileLookup) {
|
||||||
// new TouchAction<>(getDriver())
|
// new TouchAction<>(getDriver())
|
||||||
// .tap(tapOptions().withElement(element(waitForLazyElement(path, mobileLookup))))
|
// .tap(tapOptions().withElement(element(waitForLazyElement(path, mobileLookup))))
|
||||||
// .perform();
|
// .perform();
|
||||||
// TODO: actually not supported
|
// TODO: actually not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebElement waitForLazyElement(String path, MobileLookup mobileLookup) {
|
protected WebElement waitForLazyElement(String path, MobileLookup mobileLookup) {
|
||||||
doWaitForLazyElement(path, mobileLookup);
|
doWaitForLazyElement(path, mobileLookup);
|
||||||
return getDriver().findElement(resolveMobileLookup(mobileLookup, path));
|
return getDriver().findElement(resolveMobileLookup(mobileLookup, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WebElement> waitForLazyElements(String path, MobileLookup mobileLookup) {
|
List<WebElement> waitForLazyElements(String path, MobileLookup mobileLookup) {
|
||||||
doWaitForLazyElement(path, mobileLookup);
|
doWaitForLazyElement(path, mobileLookup);
|
||||||
return getDriver().findElements(resolveMobileLookup(mobileLookup, path));
|
return getDriver().findElements(resolveMobileLookup(mobileLookup, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doWaitForLazyElement(String path, MobileLookup mobileLookup) {
|
private void doWaitForLazyElement(String path, MobileLookup mobileLookup) {
|
||||||
new FluentWait<>(getDriver())
|
new FluentWait<>(getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(LAZY_ELEMENT_RENDER_TIMEOUT))
|
.withTimeout(Duration.ofSeconds(LAZY_ELEMENT_RENDER_TIMEOUT))
|
||||||
.pollingEvery(ofMillis(300))
|
.pollingEvery(ofMillis(300))
|
||||||
.ignoring(NoSuchElementException.class)
|
.ignoring(NoSuchElementException.class)
|
||||||
.ignoring(StaleElementReferenceException.class)
|
.ignoring(StaleElementReferenceException.class)
|
||||||
.withMessage("Element/s not found within " + LAZY_ELEMENT_RENDER_TIMEOUT
|
.withMessage("Element/s not found within " + LAZY_ELEMENT_RENDER_TIMEOUT
|
||||||
+ " seconds timeout. If this error occurs, you might want to consider using explicit @AndroidWait or @IosWait annotation. " + path)
|
+ " seconds timeout. If this error occurs, you might want to consider using explicit @AndroidWait or @IosWait annotation. " + path)
|
||||||
.until(driver -> !driver.findElements(resolveMobileLookup(mobileLookup, path)).isEmpty());
|
.until(driver -> !driver.findElements(resolveMobileLookup(mobileLookup, path)).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Alert waitForAlert(int timeout) {
|
private Alert waitForAlert(int timeout) {
|
||||||
return new FluentWait<>(getDriver())
|
return new FluentWait<>(getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(timeout))
|
.withTimeout(Duration.ofSeconds(timeout))
|
||||||
.pollingEvery(Duration.ofMillis(300))
|
.pollingEvery(Duration.ofMillis(300))
|
||||||
.withMessage("Alert is not present after " + timeout + " seconds timeout.")
|
.withMessage("Alert is not present after " + timeout + " seconds timeout.")
|
||||||
.until(driver -> alertIsPresent().apply(getDriver()));
|
.until(driver -> alertIsPresent().apply(getDriver()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptAlert(int timeout) {
|
public void acceptAlert(int timeout) {
|
||||||
waitForAlert(timeout).accept();
|
waitForAlert(timeout).accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dismissAlert(int timeout) {
|
public void dismissAlert(int timeout) {
|
||||||
waitForAlert(timeout).dismiss();
|
waitForAlert(timeout).dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(String path, MobileLookup mobileLookup) {
|
public String getText(String path, MobileLookup mobileLookup) {
|
||||||
return waitForLazyElement(path, mobileLookup).getText();
|
return waitForLazyElement(path, mobileLookup).getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForElements(int timeoutSeconds, MobileLookup mobileLookup, Until until, String... elementsToCheck) {
|
public void waitForElements(int timeoutSeconds, MobileLookup mobileLookup, Until until, String... elementsToCheck) {
|
||||||
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeoutSeconds));
|
WebDriverWait wait = new WebDriverWait(getDriver(), Duration.ofSeconds(timeoutSeconds));
|
||||||
Arrays.stream(elementsToCheck)
|
Arrays.stream(elementsToCheck)
|
||||||
.forEach(path -> wait.until(resolveUntil(until).apply(resolveMobileLookup(mobileLookup, path))));
|
.forEach(path -> wait.until(resolveUntil(until).apply(resolveMobileLookup(mobileLookup, path))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollRight() {
|
public void scrollRight() {
|
||||||
horizontalScroll(0.2, 0.8, 0.5);
|
horizontalScroll(0.2, 0.8, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollLeft() {
|
public void scrollLeft() {
|
||||||
horizontalScroll(0.8, 0.2, 0.5);
|
horizontalScroll(0.8, 0.2, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeViewLeft() {
|
public void swipeViewLeft() {
|
||||||
horizontalScroll(0.9, 0.02, 0.88);
|
horizontalScroll(0.9, 0.02, 0.88);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeViewRight() {
|
public void swipeViewRight() {
|
||||||
horizontalScroll(0.02, 0.9, 0.88);
|
horizontalScroll(0.02, 0.9, 0.88);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void horizontalScroll(double startPercentage, double endPercentage, double anchorPercentage) {
|
private void horizontalScroll(double startPercentage, double endPercentage, double anchorPercentage) {
|
||||||
Dimension size = getDriver().manage().window().getSize();
|
Dimension size = getDriver().manage().window().getSize();
|
||||||
int anchor = (int) (size.height * anchorPercentage);
|
int anchor = (int) (size.height * anchorPercentage);
|
||||||
int startPoint = (int) (size.width * startPercentage);
|
int startPoint = (int) (size.width * startPercentage);
|
||||||
int endPoint = (int) (size.width * endPercentage);
|
int endPoint = (int) (size.width * endPercentage);
|
||||||
|
|
||||||
doScroll(point(startPoint, anchor), point(endPoint, anchor));
|
doScroll(point(startPoint, anchor), point(endPoint, anchor));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollDown() {
|
public void scrollDown() {
|
||||||
verticalScroll(0.5, 0.2);
|
verticalScroll(0.5, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollUp() {
|
public void scrollUp() {
|
||||||
verticalScroll(0.2, 0.5);
|
verticalScroll(0.2, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollLeftUntil(String path, MobileLookup mobileLookup) {
|
public void scrollLeftUntil(String path, MobileLookup mobileLookup) {
|
||||||
doScrollUntil(path, mobileLookup, this::scrollLeft);
|
doScrollUntil(path, mobileLookup, this::scrollLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollRightUntil(String path, MobileLookup mobileLookup) {
|
public void scrollRightUntil(String path, MobileLookup mobileLookup) {
|
||||||
doScrollUntil(path, mobileLookup, this::scrollRight);
|
doScrollUntil(path, mobileLookup, this::scrollRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollDownUntil(String path, MobileLookup mobileLookup) {
|
public void scrollDownUntil(String path, MobileLookup mobileLookup) {
|
||||||
doScrollUntil(path, mobileLookup, this::scrollDown);
|
doScrollUntil(path, mobileLookup, this::scrollDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollUpUntil(String path, MobileLookup mobileLookup) {
|
public void scrollUpUntil(String path, MobileLookup mobileLookup) {
|
||||||
doScrollUntil(path, mobileLookup, this::scrollUp);
|
doScrollUntil(path, mobileLookup, this::scrollUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeFromToElement(String source, String target, MobileLookup mobileLookup) {
|
public void swipeFromToElement(String source, String target, MobileLookup mobileLookup) {
|
||||||
// Point fromElementPosition = waitForLazyElement(source, mobileLookup).getCenter();
|
// Point fromElementPosition = waitForLazyElement(source, mobileLookup).getCenter();
|
||||||
// Point toElementPosition = getDriver().findElement(resolveMobileLookup(mobileLookup, target)).getCenter();
|
// Point toElementPosition = getDriver().findElement(resolveMobileLookup(mobileLookup, target)).getCenter();
|
||||||
// doScroll(point(fromElementPosition.x, fromElementPosition.y), point(toElementPosition.x, toElementPosition.y));
|
// doScroll(point(fromElementPosition.x, fromElementPosition.y), point(toElementPosition.x, toElementPosition.y));
|
||||||
// TODO: actually not supported
|
// TODO: actually not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doScrollUntil(String path, MobileLookup mobileLookup, Runnable scroll) {
|
private void doScrollUntil(String path, MobileLookup mobileLookup, Runnable scroll) {
|
||||||
Stream.generate(() -> scroll)
|
Stream.generate(() -> scroll)
|
||||||
.limit(10)
|
.limit(10)
|
||||||
.peek(Runnable::run)
|
.peek(Runnable::run)
|
||||||
.filter(r -> isNotEmpty(getDriver().findElements(resolveMobileLookup(mobileLookup, path))))
|
.filter(r -> isNotEmpty(getDriver().findElements(resolveMobileLookup(mobileLookup, path))))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() ->
|
.orElseThrow(() ->
|
||||||
new HarnessException(String.format("Element '%s' not present after 10 swipes", path)));
|
new HarnessException(String.format("Element '%s' not present after 10 swipes", path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verticalScroll(double startPercentage, double endPercentage) {
|
private void verticalScroll(double startPercentage, double endPercentage) {
|
||||||
Dimension size = getDriver().manage().window().getSize();
|
Dimension size = getDriver().manage().window().getSize();
|
||||||
int anchor = (int) (size.width * 0.3);
|
int anchor = (int) (size.width * 0.3);
|
||||||
int startPoint = (int) (size.height * startPercentage);
|
int startPoint = (int) (size.height * startPercentage);
|
||||||
int endPoint = (int) (size.height * endPercentage);
|
int endPoint = (int) (size.height * endPercentage);
|
||||||
|
|
||||||
doScroll(point(anchor, startPoint), point(anchor, endPoint));
|
doScroll(point(anchor, startPoint), point(anchor, endPoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doScroll(PointOption from, PointOption to) {
|
private void doScroll(PointOption from, PointOption to) {
|
||||||
// new TouchAction(getDriver())
|
// new TouchAction(getDriver())
|
||||||
// .press(from)
|
// .press(from)
|
||||||
// .waitAction(waitOptions(ofMillis(1000)))
|
// .waitAction(waitOptions(ofMillis(1000)))
|
||||||
// .moveTo(to)
|
// .moveTo(to)
|
||||||
// .release().perform();
|
// .release().perform();
|
||||||
// TODO: acctualy not supported
|
// TODO: acctualy not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
public void takeSnapshot(String snapshotFileName) {
|
public void takeSnapshot(String snapshotFileName) {
|
||||||
String snapshotsPath = store.getConfig("appium.snapshots.path", "target/screenshots");
|
String snapshotsPath = store.getConfig("appium.snapshots.path", "target/screenshots");
|
||||||
File file = Paths.get(snapshotsPath, snapshotFileName).toFile();
|
File file = Paths.get(snapshotsPath, snapshotFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(file, getDriver().getScreenshotAs(OutputType.BYTES));
|
FileUtils.writeByteArrayToFile(file, getDriver().getScreenshotAs(OutputType.BYTES));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write " + snapshotFileName + " to " + snapshotsPath, e);
|
throw new IllegalStateException("Unable to write " + snapshotFileName + " to " + snapshotsPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveSource(String sourceFileName) {
|
public void saveSource(String sourceFileName) {
|
||||||
String sourcesPath = store.getConfig("appium.view.sources.path", "target/sources");
|
String sourcesPath = store.getConfig("appium.view.sources.path", "target/sources");
|
||||||
File file = Paths.get(sourcesPath, sourceFileName).toFile();
|
File file = Paths.get(sourcesPath, sourceFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(file, getDriver().getPageSource().getBytes());
|
FileUtils.writeByteArrayToFile(file, getDriver().getPageSource().getBytes());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write " + sourceFileName + " to " + sourcesPath, e);
|
throw new IllegalStateException("Unable to write " + sourceFileName + " to " + sourcesPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(TextContainer input, String text, boolean clear, MobileLookup mobileLookup) {
|
public void type(TextContainer input, String text, boolean clear, MobileLookup mobileLookup) {
|
||||||
if (clear) {
|
if (clear) {
|
||||||
clearInput(input, mobileLookup);
|
clearInput(input, mobileLookup);
|
||||||
}
|
}
|
||||||
waitForLazyElement(input.getPath(), mobileLookup).sendKeys(text);
|
waitForLazyElement(input.getPath(), mobileLookup).sendKeys(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearInput(TextContainer input, MobileLookup mobileLookup) {
|
private void clearInput(TextContainer input, MobileLookup mobileLookup) {
|
||||||
waitForLazyElement(input.getPath(), mobileLookup).clear();
|
waitForLazyElement(input.getPath(), mobileLookup).clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebElementsCheck elementsCheck() {
|
public WebElementsCheck elementsCheck() {
|
||||||
return new WebElementsCheck(this);
|
return new WebElementsCheck(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private By resolveMobileLookup(MobileLookup mobileLookup, String path) {
|
private By resolveMobileLookup(MobileLookup mobileLookup, String path) {
|
||||||
if (MobilePlatform.isAndroid(getStore())) {
|
if (MobilePlatform.isAndroid(getStore())) {
|
||||||
path = mobileLookup.equals(MobileLookup.ANDROID_TEXT) ? "new UiSelector().textContains(\"" + path + "\")" : path;
|
path = mobileLookup.equals(MobileLookup.ANDROID_TEXT) ? "new UiSelector().textContains(\"" + path + "\")" : path;
|
||||||
path = mobileLookup.equals(MobileLookup.ID) ? bundleId + ":id/" + path : path;
|
path = mobileLookup.equals(MobileLookup.ID) ? bundleId + ":id/" + path : path;
|
||||||
}
|
}
|
||||||
return resolveLookup(mobileLookup).apply(path);
|
return resolveLookup(mobileLookup).apply(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<String, By> resolveLookup(MobileLookup mobileLookup) {
|
private Function<String, By> resolveLookup(MobileLookup mobileLookup) {
|
||||||
switch (mobileLookup) {
|
switch (mobileLookup) {
|
||||||
case XPATH:
|
case XPATH:
|
||||||
return MobileBy::xpath;
|
return MobileBy::xpath;
|
||||||
case ID:
|
case ID:
|
||||||
return MobileBy::id;
|
return MobileBy::id;
|
||||||
case IOS_NAME:
|
case IOS_NAME:
|
||||||
return MobileBy::name;
|
return MobileBy::name;
|
||||||
case ANDROID_TEXT:
|
case ANDROID_TEXT:
|
||||||
return MobileBy::AndroidUIAutomator;
|
return MobileBy::AndroidUIAutomator;
|
||||||
default:
|
default:
|
||||||
return defaultLookup;
|
return defaultLookup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<By, ExpectedCondition<?>> resolveUntil(Until until) {
|
private Function<By, ExpectedCondition<?>> resolveUntil(Until until) {
|
||||||
switch (until) {
|
switch (until) {
|
||||||
case VISIBLE:
|
case VISIBLE:
|
||||||
return ExpectedConditions::visibilityOfElementLocated;
|
return ExpectedConditions::visibilityOfElementLocated;
|
||||||
case PRESENT_IN_DOM:
|
case PRESENT_IN_DOM:
|
||||||
return ExpectedConditions::presenceOfElementLocated;
|
return ExpectedConditions::presenceOfElementLocated;
|
||||||
case GONE:
|
case GONE:
|
||||||
return ExpectedConditions::invisibilityOfElementLocated;
|
return ExpectedConditions::invisibilityOfElementLocated;
|
||||||
default:
|
default:
|
||||||
return ExpectedConditions::presenceOfElementLocated;
|
return ExpectedConditions::presenceOfElementLocated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile;
|
package cz.moneta.test.harness.connectors.mobile;
|
||||||
|
|
||||||
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
||||||
import org.openqa.selenium.Platform;
|
import org.openqa.selenium.Platform;
|
||||||
|
|
||||||
public class UnsupportedPlatformException extends AssertionError {
|
public class UnsupportedPlatformException extends AssertionError {
|
||||||
|
|
||||||
public UnsupportedPlatformException(MobilePlatform platform) {
|
public UnsupportedPlatformException(MobilePlatform platform) {
|
||||||
super("Method is supported only for " + platform + ", please check your mobile platform or use another method");
|
super("Method is supported only for " + platform + ", please check your mobile platform or use another method");
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnsupportedPlatformException(String message) {
|
public UnsupportedPlatformException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,28 +1,28 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile;
|
package cz.moneta.test.harness.connectors.mobile;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WebElementsCheck {
|
public class WebElementsCheck {
|
||||||
|
|
||||||
private final AppiumMobileConnector connector;
|
private final AppiumMobileConnector connector;
|
||||||
|
|
||||||
public WebElementsCheck(AppiumMobileConnector connector) {
|
public WebElementsCheck(AppiumMobileConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementContent(String path, String content, MobileLookup mobileLookup) {
|
public void checkElementContent(String path, String content, MobileLookup mobileLookup) {
|
||||||
List<WebElement> elements = connector.waitForLazyElements(path, mobileLookup);
|
List<WebElement> elements = connector.waitForLazyElements(path, mobileLookup);
|
||||||
elements.stream()
|
elements.stream()
|
||||||
.map(WebElement::getText)
|
.map(WebElement::getText)
|
||||||
.filter(text -> text.contains(content))
|
.filter(text -> text.contains(content))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new AssertionError(String.format("Cannot find element that contains text %s", content)));
|
.orElseThrow(() -> new AssertionError(String.format("Cannot find element that contains text %s", content)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementPresent(String path, MobileLookup mobileLookup) {
|
public void checkElementPresent(String path, MobileLookup mobileLookup) {
|
||||||
connector.waitForLazyElement(path, mobileLookup);
|
connector.waitForLazyElement(path, mobileLookup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,123 +1,123 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile.android;
|
package cz.moneta.test.harness.connectors.mobile.android;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import io.appium.java_client.android.AndroidDriver;
|
import io.appium.java_client.android.AndroidDriver;
|
||||||
import io.appium.java_client.android.nativekey.AndroidKey;
|
import io.appium.java_client.android.nativekey.AndroidKey;
|
||||||
import io.appium.java_client.android.nativekey.KeyEvent;
|
import io.appium.java_client.android.nativekey.KeyEvent;
|
||||||
import io.appium.java_client.remote.AutomationName;
|
import io.appium.java_client.remote.AutomationName;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.Platform;
|
import org.openqa.selenium.Platform;
|
||||||
import org.openqa.selenium.WebDriverException;
|
import org.openqa.selenium.WebDriverException;
|
||||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class AndroidConnector extends AppiumMobileConnector<AndroidDriver> {
|
public class AndroidConnector extends AppiumMobileConnector<AndroidDriver> {
|
||||||
private final AndroidDriver driver;
|
private final AndroidDriver driver;
|
||||||
|
|
||||||
public AndroidConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String deviceName, String udid, String appPackage, String appBinaryPath) {
|
public AndroidConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String deviceName, String udid, String appPackage, String appBinaryPath) {
|
||||||
super(defaultLookup, store, host, appBinaryPath);
|
super(defaultLookup, store, host, appBinaryPath);
|
||||||
this.deviceName = deviceName;
|
this.deviceName = deviceName;
|
||||||
this.udid = udid;
|
this.udid = udid;
|
||||||
this.bundleId = appPackage;
|
this.bundleId = appPackage;
|
||||||
this.driver = initDriver();
|
this.driver = initDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AndroidDriver getDriver() {
|
protected AndroidDriver getDriver() {
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AndroidDriver initDriver() {
|
private AndroidDriver initDriver() {
|
||||||
return startDriver()
|
return startDriver()
|
||||||
.map(this::startRecording)
|
.map(this::startRecording)
|
||||||
.flatMap(d ->
|
.flatMap(d ->
|
||||||
Stream.<Supplier<Optional<AndroidDriver>>>of(
|
Stream.<Supplier<Optional<AndroidDriver>>>of(
|
||||||
() -> launchExistingApp(d))
|
() -> launchExistingApp(d))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.orElseThrow(() -> new HarnessException("App was not started"));
|
.orElseThrow(() -> new HarnessException("App was not started"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<AndroidDriver> startDriver() {
|
private Optional<AndroidDriver> startDriver() {
|
||||||
return Optional.ofNullable(host)
|
return Optional.ofNullable(host)
|
||||||
.map(cs -> {
|
.map(cs -> {
|
||||||
DesiredCapabilities capabilities = new DesiredCapabilities();
|
DesiredCapabilities capabilities = new DesiredCapabilities();
|
||||||
mobileLanguage(capabilities, "US", "CZ");
|
mobileLanguage(capabilities, "US", "CZ");
|
||||||
setDesiredCapabilities(Platform.ANDROID, deviceName, udid, getUiAutomator(), capabilities);
|
setDesiredCapabilities(Platform.ANDROID, deviceName, udid, getUiAutomator(), capabilities);
|
||||||
try {
|
try {
|
||||||
return new AndroidDriver(new URL(cs), capabilities);
|
return new AndroidDriver(new URL(cs), capabilities);
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
throw new HarnessException(String.format("Appium connection to ANDROID device is invalid: %s", cs), e);
|
throw new HarnessException(String.format("Appium connection to ANDROID device is invalid: %s", cs), e);
|
||||||
} catch (WebDriverException e) {
|
} catch (WebDriverException e) {
|
||||||
if (e.getCause() instanceof ConnectException) {
|
if (e.getCause() instanceof ConnectException) {
|
||||||
throw new HarnessException(String.format("Cannot connect to Appium on %s. Is your Appium Desktop running?", cs), e);
|
throw new HarnessException(String.format("Cannot connect to Appium on %s. Is your Appium Desktop running?", cs), e);
|
||||||
} else {
|
} else {
|
||||||
throw new HarnessException("Android driver didn't start, please check the connect to server", e);
|
throw new HarnessException("Android driver didn't start, please check the connect to server", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void captureVideo(String videoFileName) {
|
public void captureVideo(String videoFileName) {
|
||||||
if (!checkVideoRecord()) {
|
if (!checkVideoRecord()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String videoPath = store.getConfig("appium.video.path", "target/videos");
|
String videoPath = store.getConfig("appium.video.path", "target/videos");
|
||||||
File file = Paths.get(videoPath, videoFileName).toFile();
|
File file = Paths.get(videoPath, videoFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(file, Base64.getDecoder().decode(driver.stopRecordingScreen()));
|
FileUtils.writeByteArrayToFile(file, Base64.getDecoder().decode(driver.stopRecordingScreen()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write " + videoFileName + " to " + videoPath, e);
|
throw new IllegalStateException("Unable to write " + videoFileName + " to " + videoPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetApp() {
|
public void resetApp() {
|
||||||
driver.resetApp();
|
driver.resetApp();
|
||||||
driver.activateApp(bundleId);
|
driver.activateApp(bundleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
driver.quit();
|
driver.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideAndroidKeyboard() {
|
public void hideAndroidKeyboard() {
|
||||||
driver.hideKeyboard();
|
driver.hideKeyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AndroidDriver startRecording(AndroidDriver driver) {
|
private AndroidDriver startRecording(AndroidDriver driver) {
|
||||||
if (checkVideoRecord()) {
|
if (checkVideoRecord()) {
|
||||||
driver.startRecordingScreen();
|
driver.startRecordingScreen();
|
||||||
}
|
}
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getUiAutomator() {
|
private String getUiAutomator() {
|
||||||
boolean uiAutomator = Boolean.parseBoolean(store.getConfig("appium.android.uiAutomator", "false"));
|
boolean uiAutomator = Boolean.parseBoolean(store.getConfig("appium.android.uiAutomator", "false"));
|
||||||
return uiAutomator ? AutomationName.ANDROID_UIAUTOMATOR2 : null;
|
return uiAutomator ? AutomationName.ANDROID_UIAUTOMATOR2 : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pressAndroidKeys(AndroidKey... keys) {
|
public void pressAndroidKeys(AndroidKey... keys) {
|
||||||
Arrays.stream(keys)
|
Arrays.stream(keys)
|
||||||
.forEach(key -> getDriver().pressKey(new KeyEvent(key)));
|
.forEach(key -> getDriver().pressKey(new KeyEvent(key)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile.ios;
|
package cz.moneta.test.harness.connectors.mobile.ios;
|
||||||
|
|
||||||
public enum HideIosKeyboardButton {
|
public enum HideIosKeyboardButton {
|
||||||
DONE("Done"),
|
DONE("Done"),
|
||||||
GO("Go"),
|
GO("Go"),
|
||||||
ENTER("Enter"),
|
ENTER("Enter"),
|
||||||
RETURN("Return"),
|
RETURN("Return"),
|
||||||
NONE("");
|
NONE("");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
HideIosKeyboardButton(String value) {
|
HideIosKeyboardButton(String value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,134 +1,134 @@
|
|||||||
package cz.moneta.test.harness.connectors.mobile.ios;
|
package cz.moneta.test.harness.connectors.mobile.ios;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
||||||
import io.appium.java_client.ios.IOSDriver;
|
import io.appium.java_client.ios.IOSDriver;
|
||||||
import io.appium.java_client.remote.AutomationName;
|
import io.appium.java_client.remote.AutomationName;
|
||||||
import io.appium.java_client.remote.IOSMobileCapabilityType;
|
import io.appium.java_client.remote.IOSMobileCapabilityType;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class IosConnector extends AppiumMobileConnector<IOSDriver> {
|
public class IosConnector extends AppiumMobileConnector<IOSDriver> {
|
||||||
private IOSDriver iosDriver;
|
private IOSDriver iosDriver;
|
||||||
|
|
||||||
public IosConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String deviceName, String udid, String bundleId, String appBinaryPath) {
|
public IosConnector(Function<String, By> defaultLookup, StoreAccessor store, String host, String deviceName, String udid, String bundleId, String appBinaryPath) {
|
||||||
super(defaultLookup, store, host, appBinaryPath);
|
super(defaultLookup, store, host, appBinaryPath);
|
||||||
this.udid = udid;
|
this.udid = udid;
|
||||||
this.bundleId = bundleId;
|
this.bundleId = bundleId;
|
||||||
this.deviceName = deviceName;
|
this.deviceName = deviceName;
|
||||||
this.iosDriver = initDriver();
|
this.iosDriver = initDriver();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IOSDriver getDriver() {
|
protected IOSDriver getDriver() {
|
||||||
return iosDriver;
|
return iosDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IOSDriver initDriver() {
|
private IOSDriver initDriver() {
|
||||||
return startDriver()
|
return startDriver()
|
||||||
.map(this::startRecording)
|
.map(this::startRecording)
|
||||||
.flatMap(d -> Stream.<Supplier<Optional<IOSDriver>>>of(
|
.flatMap(d -> Stream.<Supplier<Optional<IOSDriver>>>of(
|
||||||
() -> launchExistingApp(d))
|
() -> launchExistingApp(d))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.orElseThrow(() -> new HarnessException("App was not started"));
|
.orElseThrow(() -> new HarnessException("App was not started"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<IOSDriver> startDriver() {
|
private Optional<IOSDriver> startDriver() {
|
||||||
return Optional.ofNullable(host)
|
return Optional.ofNullable(host)
|
||||||
.map(cs -> {
|
.map(cs -> {
|
||||||
DesiredCapabilities capabilities = new DesiredCapabilities();
|
DesiredCapabilities capabilities = new DesiredCapabilities();
|
||||||
setDesiredCapabilities(Platform.IOS, deviceName, udid, AutomationName.IOS_XCUI_TEST, capabilities);
|
setDesiredCapabilities(Platform.IOS, deviceName, udid, AutomationName.IOS_XCUI_TEST, capabilities);
|
||||||
mobileLanguage(capabilities, "en_US", "cs_CZ");
|
mobileLanguage(capabilities, "en_US", "cs_CZ");
|
||||||
capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID, this.bundleId);
|
capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID, this.bundleId);
|
||||||
try {
|
try {
|
||||||
return new IOSDriver(new URL(cs), capabilities);
|
return new IOSDriver(new URL(cs), capabilities);
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
throw new HarnessException(String.format("Appium connection to IOS device is invalid: %s", cs), e);
|
throw new HarnessException(String.format("Appium connection to IOS device is invalid: %s", cs), e);
|
||||||
} catch (WebDriverException e) {
|
} catch (WebDriverException e) {
|
||||||
if (e.getCause() instanceof ConnectException) {
|
if (e.getCause() instanceof ConnectException) {
|
||||||
throw new HarnessException(String.format("Cannot connect to Appium on %s. Is your Appium Desktop running?", cs), e);
|
throw new HarnessException(String.format("Cannot connect to Appium on %s. Is your Appium Desktop running?", cs), e);
|
||||||
} else {
|
} else {
|
||||||
throw new HarnessException("Ios driver didn't start, please check the connect to server", e);
|
throw new HarnessException("Ios driver didn't start, please check the connect to server", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void captureVideo(String videoFileName) {
|
public void captureVideo(String videoFileName) {
|
||||||
if (!checkVideoRecord()) {
|
if (!checkVideoRecord()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String videoPath = store.getConfig("appium.video.path", "target/videos");
|
String videoPath = store.getConfig("appium.video.path", "target/videos");
|
||||||
File file = Paths.get(videoPath, videoFileName).toFile();
|
File file = Paths.get(videoPath, videoFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(file, Base64.getDecoder().decode(iosDriver.stopRecordingScreen()));
|
FileUtils.writeByteArrayToFile(file, Base64.getDecoder().decode(iosDriver.stopRecordingScreen()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write " + videoFileName + " to " + videoPath, e);
|
throw new IllegalStateException("Unable to write " + videoFileName + " to " + videoPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IOSDriver startRecording(IOSDriver driver) {
|
private IOSDriver startRecording(IOSDriver driver) {
|
||||||
if (checkVideoRecord()) {
|
if (checkVideoRecord()) {
|
||||||
driver.startRecordingScreen();
|
driver.startRecordingScreen();
|
||||||
}
|
}
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetApp() {
|
public void resetApp() {
|
||||||
iosDriver.removeApp(bundleId);
|
iosDriver.removeApp(bundleId);
|
||||||
iosDriver.installApp(appBinaryPath);
|
iosDriver.installApp(appBinaryPath);
|
||||||
iosDriver.activateApp(bundleId);
|
iosDriver.activateApp(bundleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
iosDriver.closeApp();
|
iosDriver.closeApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hideIosKeyboard(HideIosKeyboardButton hideIosKeyboardButton) {
|
public void hideIosKeyboard(HideIosKeyboardButton hideIosKeyboardButton) {
|
||||||
click(hideIosKeyboardButton.getValue(), MobileLookup.ID);
|
click(hideIosKeyboardButton.getValue(), MobileLookup.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeScrollScript(String name, String direction) {
|
public void executeScrollScript(String name, String direction) {
|
||||||
Map<String, Object> args = new HashMap<>();
|
Map<String, Object> args = new HashMap<>();
|
||||||
args.put("direction", direction);
|
args.put("direction", direction);
|
||||||
args.put("name", name);
|
args.put("name", name);
|
||||||
Optional.of(getDriver())
|
Optional.of(getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.ifPresent(script -> {
|
.ifPresent(script -> {
|
||||||
try {
|
try {
|
||||||
script.executeScript("mobile: scroll", args);
|
script.executeScript("mobile: scroll", args);
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
throw new HarnessException("Element " + name + " is not present");
|
throw new HarnessException("Element " + name + " is not present");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add in the future
|
//TODO add in the future
|
||||||
public void pressIosKeys() {
|
public void pressIosKeys() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,62 +1,62 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.Connector;
|
import cz.moneta.test.harness.connectors.Connector;
|
||||||
import org.apache.logging.log4j.jul.LogManager;
|
import org.apache.logging.log4j.jul.LogManager;
|
||||||
import org.glassfish.jersey.SslConfigurator;
|
import org.glassfish.jersey.SslConfigurator;
|
||||||
import org.glassfish.jersey.client.ClientConfig;
|
import org.glassfish.jersey.client.ClientConfig;
|
||||||
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
|
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
|
||||||
import org.glassfish.jersey.jackson.JacksonFeature;
|
import org.glassfish.jersey.jackson.JacksonFeature;
|
||||||
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
|
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
|
||||||
import org.glassfish.jersey.logging.LoggingFeature;
|
import org.glassfish.jersey.logging.LoggingFeature;
|
||||||
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
import org.glassfish.jersey.media.multipart.MultiPartFeature;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Client;
|
import jakarta.ws.rs.client.Client;
|
||||||
import jakarta.ws.rs.client.ClientBuilder;
|
import jakarta.ws.rs.client.ClientBuilder;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.DEFAULT_REST_READ_TIMEOUT;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.DEFAULT_REST_READ_TIMEOUT;
|
||||||
|
|
||||||
public abstract class BaseRestConnector implements Connector {
|
public abstract class BaseRestConnector implements Connector {
|
||||||
|
|
||||||
protected Client createHttpClient(String loggerName) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
protected Client createHttpClient(String loggerName) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
||||||
return createHttpClient(loggerName, DEFAULT_REST_READ_TIMEOUT);
|
return createHttpClient(loggerName, DEFAULT_REST_READ_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Client createHttpClient(String loggerName, long readTimeout) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
protected Client createHttpClient(String loggerName, long readTimeout) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
||||||
KeyStore trustedStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore trustedStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
|
||||||
trustedStore.load(getInputStream("keystores/mb_root"), "changeit".toCharArray());
|
trustedStore.load(getInputStream("keystores/mb_root"), "changeit".toCharArray());
|
||||||
keyStore.load(getInputStream("keystores/api_gw_client.p12"), "changeit".toCharArray());
|
keyStore.load(getInputStream("keystores/api_gw_client.p12"), "changeit".toCharArray());
|
||||||
|
|
||||||
ClientConfig config = new ClientConfig()
|
ClientConfig config = new ClientConfig()
|
||||||
.register(JacksonFeature.class)
|
.register(JacksonFeature.class)
|
||||||
.register(JacksonJsonProvider.class)
|
.register(JacksonJsonProvider.class)
|
||||||
.register(MultiPartFeature.class)
|
.register(MultiPartFeature.class)
|
||||||
.register(new LoggingFeature(LogManager.getLogManager().getLogger(loggerName),
|
.register(new LoggingFeature(LogManager.getLogManager().getLogger(loggerName),
|
||||||
Level.FINE, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024 * 1024))
|
Level.FINE, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024 * 1024))
|
||||||
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); // TODO: test it, this line may affect all REST calls
|
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); // TODO: test it, this line may affect all REST calls
|
||||||
|
|
||||||
return ClientBuilder.newBuilder()
|
return ClientBuilder.newBuilder()
|
||||||
.withConfig(config)
|
.withConfig(config)
|
||||||
.connectTimeout(10, TimeUnit.SECONDS)
|
.connectTimeout(10, TimeUnit.SECONDS)
|
||||||
.readTimeout(readTimeout, TimeUnit.SECONDS)
|
.readTimeout(readTimeout, TimeUnit.SECONDS)
|
||||||
.sslContext(SslConfigurator.newInstance()
|
.sslContext(SslConfigurator.newInstance()
|
||||||
.trustStore(trustedStore)
|
.trustStore(trustedStore)
|
||||||
.keyStore(keyStore)
|
.keyStore(keyStore)
|
||||||
.keyStorePassword("changeit".toCharArray())
|
.keyStorePassword("changeit".toCharArray())
|
||||||
.createSSLContext())
|
.createSSLContext())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream(String name) {
|
public InputStream getInputStream(String name) {
|
||||||
return this.getClass().getClassLoader().getResourceAsStream(name);
|
return this.getClass().getClassLoader().getResourceAsStream(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,38 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.InternalServerErrorException;
|
import jakarta.ws.rs.InternalServerErrorException;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rest connector returning extendable object. Now the response contains body and response headers.
|
* Rest connector returning extendable object. Now the response contains body and response headers.
|
||||||
*/
|
*/
|
||||||
public class ExtendedRestConnector extends SimpleRestConnector implements RestConnector {
|
public class ExtendedRestConnector extends SimpleRestConnector implements RestConnector {
|
||||||
|
|
||||||
public ExtendedRestConnector(String endpointUrl, String loggerName) {
|
public ExtendedRestConnector(String endpointUrl, String loggerName) {
|
||||||
super(endpointUrl, loggerName);
|
super(endpointUrl, loggerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedRestConnector(String endpointUrl, String loggerName, long readTimeout) {
|
public ExtendedRestConnector(String endpointUrl, String loggerName, long readTimeout) {
|
||||||
super(endpointUrl, loggerName, readTimeout);
|
super(endpointUrl, loggerName, readTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(String path, Entity<?> request,
|
public <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(String path, Entity<?> request,
|
||||||
Class<T> responseType,
|
Class<T> responseType,
|
||||||
Map<String, Object> headers) {
|
Map<String, Object> headers) {
|
||||||
try {
|
try {
|
||||||
Response response = doPost(path, request, headers);
|
Response response = doPost(path, request, headers);
|
||||||
|
|
||||||
return Pair.of(response.getStatus(), new ExtendedRestResponse<>(response.readEntity(responseType),
|
return Pair.of(response.getStatus(), new ExtendedRestResponse<>(response.readEntity(responseType),
|
||||||
response.getHeaders()));
|
response.getHeaders()));
|
||||||
} catch (InternalServerErrorException serverError) {
|
} catch (InternalServerErrorException serverError) {
|
||||||
return throwWithResponseText(serverError);
|
return throwWithResponseText(serverError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,25 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.MultivaluedMap;
|
import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extendable response object from {@link ExtendedRestConnector}.
|
* Extendable response object from {@link ExtendedRestConnector}.
|
||||||
*/
|
*/
|
||||||
public class ExtendedRestResponse<T> {
|
public class ExtendedRestResponse<T> {
|
||||||
|
|
||||||
private T responseBody;
|
private T responseBody;
|
||||||
private MultivaluedMap<String, Object> responseHeaders;
|
private MultivaluedMap<String, Object> responseHeaders;
|
||||||
|
|
||||||
public ExtendedRestResponse(T responseBody, MultivaluedMap<String, Object> responseHeaders) {
|
public ExtendedRestResponse(T responseBody, MultivaluedMap<String, Object> responseHeaders) {
|
||||||
this.responseBody = responseBody;
|
this.responseBody = responseBody;
|
||||||
this.responseHeaders = responseHeaders;
|
this.responseHeaders = responseHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getResponseBody() {
|
public T getResponseBody() {
|
||||||
return responseBody;
|
return responseBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultivaluedMap<String, Object> getResponseHeaders() {
|
public MultivaluedMap<String, Object> getResponseHeaders() {
|
||||||
return responseHeaders;
|
return responseHeaders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class RemoteRestCallRequest {
|
public class RemoteRestCallRequest {
|
||||||
private String path;
|
private String path;
|
||||||
private Object request;
|
private Object request;
|
||||||
private Map<String, Object> getProperties;
|
private Map<String, Object> getProperties;
|
||||||
private Map<String, Object> headers;
|
private Map<String, Object> headers;
|
||||||
|
|
||||||
public RemoteRestCallRequest() {}
|
public RemoteRestCallRequest() {}
|
||||||
|
|
||||||
public RemoteRestCallRequest(String path, Map<String, Object> getProperties, Map<String, Object> headers) {
|
public RemoteRestCallRequest(String path, Map<String, Object> getProperties, Map<String, Object> headers) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.getProperties = getProperties;
|
this.getProperties = getProperties;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RemoteRestCallRequest(String path, Object request, Map<String, Object> headers) {
|
public RemoteRestCallRequest(String path, Object request, Map<String, Object> headers) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getRequest() {
|
public Object getRequest() {
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getGetProperties() {
|
public Map<String, Object> getGetProperties() {
|
||||||
return getProperties;
|
return getProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> getHeaders() {
|
public Map<String, Object> getHeaders() {
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response handler which enables response verification and post-processing
|
* Response handler which enables response verification and post-processing
|
||||||
*
|
*
|
||||||
* @param <I> original request invocation (i.e. the request call)
|
* @param <I> original request invocation (i.e. the request call)
|
||||||
* @param <RESP> the original response against which the handling is performed
|
* @param <RESP> the original response against which the handling is performed
|
||||||
* @see cz.moneta.test.harness.connectors.wso2.TokenRenewalResponseHandler
|
* @see cz.moneta.test.harness.connectors.wso2.TokenRenewalResponseHandler
|
||||||
*/
|
*/
|
||||||
public interface ResponseHandler<I, RESP> {
|
public interface ResponseHandler<I, RESP> {
|
||||||
RESP handle(I invocation, RESP response);
|
RESP handle(I invocation, RESP response);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import cz.moneta.test.harness.performance.PerformanceAware;
|
import cz.moneta.test.harness.performance.PerformanceAware;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.client.Invocation;
|
import jakarta.ws.rs.client.Invocation;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface RestConnector extends PerformanceAware {
|
public interface RestConnector extends PerformanceAware {
|
||||||
<T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers);
|
||||||
|
|
||||||
<T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers);
|
||||||
|
|
||||||
<T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers);
|
||||||
|
|
||||||
<T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> repsonseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> repsonseType, Map<String, Object> headers);
|
||||||
|
|
||||||
RestConnector registerResponseHandler(
|
RestConnector registerResponseHandler(
|
||||||
ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> responseHandler);
|
ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> responseHandler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return response body with response headers.
|
* Return response body with response headers.
|
||||||
*/
|
*/
|
||||||
default <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(
|
default <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(
|
||||||
String path, Entity<?> request,
|
String path, Entity<?> request,
|
||||||
Class<T> responseType, Map<String,
|
Class<T> responseType, Map<String,
|
||||||
Object> headers) {
|
Object> headers) {
|
||||||
throw new IllegalStateException("Not implemented");
|
throw new IllegalStateException("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default String getPerformanceReportKey(Object proxy, Method method, Object[] args) {
|
default String getPerformanceReportKey(Object proxy, Method method, Object[] args) {
|
||||||
return (String) args[0];
|
return (String) args[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,155 +1,155 @@
|
|||||||
package cz.moneta.test.harness.connectors.rest;
|
package cz.moneta.test.harness.connectors.rest;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.InternalServerErrorException;
|
import jakarta.ws.rs.InternalServerErrorException;
|
||||||
import jakarta.ws.rs.ProcessingException;
|
import jakarta.ws.rs.ProcessingException;
|
||||||
import jakarta.ws.rs.client.Client;
|
import jakarta.ws.rs.client.Client;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.client.Invocation;
|
import jakarta.ws.rs.client.Invocation;
|
||||||
import jakarta.ws.rs.client.WebTarget;
|
import jakarta.ws.rs.client.WebTarget;
|
||||||
import jakarta.ws.rs.core.CacheControl;
|
import jakarta.ws.rs.core.CacheControl;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.DEFAULT_REST_READ_TIMEOUT;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.DEFAULT_REST_READ_TIMEOUT;
|
||||||
import static cz.moneta.test.harness.support.rest.RestUtils.toMultiMap;
|
import static cz.moneta.test.harness.support.rest.RestUtils.toMultiMap;
|
||||||
|
|
||||||
public class SimpleRestConnector extends BaseRestConnector implements RestConnector {
|
public class SimpleRestConnector extends BaseRestConnector implements RestConnector {
|
||||||
|
|
||||||
protected WebTarget target;
|
protected WebTarget target;
|
||||||
protected List<ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response>> responseHandlers = new ArrayList<>();
|
protected List<ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response>> responseHandlers = new ArrayList<>();
|
||||||
|
|
||||||
public SimpleRestConnector(String endpointUrl, String loggerName) {
|
public SimpleRestConnector(String endpointUrl, String loggerName) {
|
||||||
this(endpointUrl, loggerName, DEFAULT_REST_READ_TIMEOUT);
|
this(endpointUrl, loggerName, DEFAULT_REST_READ_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SimpleRestConnector(String endpointUrl, String loggerName, long readTimeout) {
|
public SimpleRestConnector(String endpointUrl, String loggerName, long readTimeout) {
|
||||||
try {
|
try {
|
||||||
Client client = createHttpClient(loggerName, readTimeout);
|
Client client = createHttpClient(loggerName, readTimeout);
|
||||||
target = client.target(endpointUrl);
|
target = client.target(endpointUrl);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("REST connector is not configured properly", e);
|
throw new RuntimeException("REST connector is not configured properly", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
try {
|
try {
|
||||||
Invocation.Builder invocation = properties.entrySet().stream()
|
Invocation.Builder invocation = properties.entrySet().stream()
|
||||||
.reduce(target, (t, e) -> t.queryParam(e.getKey(), e.getValue()), (t1, t2) -> t1)
|
.reduce(target, (t, e) -> t.queryParam(e.getKey(), e.getValue()), (t1, t2) -> t1)
|
||||||
.path(path)
|
.path(path)
|
||||||
.request()
|
.request()
|
||||||
.headers(toMultiMap(headers))
|
.headers(toMultiMap(headers))
|
||||||
.cacheControl(CacheControl.valueOf("no-cache"));
|
.cacheControl(CacheControl.valueOf("no-cache"));
|
||||||
|
|
||||||
Response response = responseHandlers.stream()
|
Response response = responseHandlers.stream()
|
||||||
.reduce(
|
.reduce(
|
||||||
invocation.get(),
|
invocation.get(),
|
||||||
(resp, h) -> h.handle(Pair.of(invocation, Invocation.Builder::get), resp),
|
(resp, h) -> h.handle(Pair.of(invocation, Invocation.Builder::get), resp),
|
||||||
(h1, h2) -> h1);
|
(h1, h2) -> h1);
|
||||||
|
|
||||||
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
||||||
} catch (InternalServerErrorException serverError) {
|
} catch (InternalServerErrorException serverError) {
|
||||||
return throwWithResponseText(serverError);
|
return throwWithResponseText(serverError);
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
try {
|
try {
|
||||||
Response response = doPost(path, request, headers);
|
Response response = doPost(path, request, headers);
|
||||||
|
|
||||||
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
||||||
} catch (InternalServerErrorException serverError) {
|
} catch (InternalServerErrorException serverError) {
|
||||||
return throwWithResponseText(serverError);
|
return throwWithResponseText(serverError);
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response doPost(String path, Entity<?> request, Map<String, Object> headers) {
|
protected Response doPost(String path, Entity<?> request, Map<String, Object> headers) {
|
||||||
Invocation.Builder invocation = target.path(path)
|
Invocation.Builder invocation = target.path(path)
|
||||||
.request()
|
.request()
|
||||||
.headers(toMultiMap(headers))
|
.headers(toMultiMap(headers))
|
||||||
.cacheControl(CacheControl.valueOf("no-cache"));
|
.cacheControl(CacheControl.valueOf("no-cache"));
|
||||||
|
|
||||||
return responseHandlers.stream()
|
return responseHandlers.stream()
|
||||||
.reduce(
|
.reduce(
|
||||||
invocation.post(request),
|
invocation.post(request),
|
||||||
(resp, h) -> h.handle(Pair.of(invocation, i -> i.post(request)), resp),
|
(resp, h) -> h.handle(Pair.of(invocation, i -> i.post(request)), resp),
|
||||||
(h1, h2) -> h1);
|
(h1, h2) -> h1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
try {
|
try {
|
||||||
Invocation.Builder invocation = properties.entrySet().stream()
|
Invocation.Builder invocation = properties.entrySet().stream()
|
||||||
.reduce(target, (t, e) -> t.queryParam(e.getKey(), e.getValue()), (t1, t2) -> t1)
|
.reduce(target, (t, e) -> t.queryParam(e.getKey(), e.getValue()), (t1, t2) -> t1)
|
||||||
.path(path)
|
.path(path)
|
||||||
.request()
|
.request()
|
||||||
.headers(toMultiMap(headers))
|
.headers(toMultiMap(headers))
|
||||||
.cacheControl(CacheControl.valueOf("no-cache"));
|
.cacheControl(CacheControl.valueOf("no-cache"));
|
||||||
|
|
||||||
Response response = responseHandlers.stream()
|
Response response = responseHandlers.stream()
|
||||||
.reduce(
|
.reduce(
|
||||||
invocation.delete(),
|
invocation.delete(),
|
||||||
(resp, h) -> h.handle(Pair.of(invocation, Invocation.Builder::delete), resp),
|
(resp, h) -> h.handle(Pair.of(invocation, Invocation.Builder::delete), resp),
|
||||||
(h1, h2) -> h1);
|
(h1, h2) -> h1);
|
||||||
|
|
||||||
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
||||||
} catch (InternalServerErrorException serverError) {
|
} catch (InternalServerErrorException serverError) {
|
||||||
return throwWithResponseText(serverError);
|
return throwWithResponseText(serverError);
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
try {
|
try {
|
||||||
Invocation.Builder invocation = target.path(path)
|
Invocation.Builder invocation = target.path(path)
|
||||||
.request()
|
.request()
|
||||||
.headers(toMultiMap(headers))
|
.headers(toMultiMap(headers))
|
||||||
.cacheControl(CacheControl.valueOf("no-cache"));
|
.cacheControl(CacheControl.valueOf("no-cache"));
|
||||||
|
|
||||||
Response response = responseHandlers.stream()
|
Response response = responseHandlers.stream()
|
||||||
.reduce(
|
.reduce(
|
||||||
invocation.method("PATCH", request),
|
invocation.method("PATCH", request),
|
||||||
(resp, h) -> h.handle(Pair.of(invocation, i -> i.method("PATCH", request)), resp),
|
(resp, h) -> h.handle(Pair.of(invocation, i -> i.method("PATCH", request)), resp),
|
||||||
(h1, h2) -> h1);
|
(h1, h2) -> h1);
|
||||||
|
|
||||||
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
||||||
} catch (InternalServerErrorException serverError) {
|
} catch (InternalServerErrorException serverError) {
|
||||||
return throwWithResponseText(serverError);
|
return throwWithResponseText(serverError);
|
||||||
} catch (ProcessingException e) {
|
} catch (ProcessingException e) {
|
||||||
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
throw new RuntimeException("Error during HTTP connection " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RestConnector registerResponseHandler(ResponseHandler<Pair<Invocation.Builder,
|
public RestConnector registerResponseHandler(ResponseHandler<Pair<Invocation.Builder,
|
||||||
Function<Invocation.Builder, Response>>, Response> responseHandler) {
|
Function<Invocation.Builder, Response>>, Response> responseHandler) {
|
||||||
responseHandlers.add(responseHandler);
|
responseHandlers.add(responseHandler);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T throwWithResponseText(InternalServerErrorException serverError) {
|
protected <T> T throwWithResponseText(InternalServerErrorException serverError) {
|
||||||
String response = "N/A";
|
String response = "N/A";
|
||||||
try {
|
try {
|
||||||
response = IOUtils.toString((InputStream) serverError.getResponse().getEntity(), Charset.defaultCharset());
|
response = IOUtils.toString((InputStream) serverError.getResponse().getEntity(), Charset.defaultCharset());
|
||||||
} catch (IOException ignore) {
|
} catch (IOException ignore) {
|
||||||
}
|
}
|
||||||
throw new AssertionError(String.format("Request failed. HTTP status: %d, response: %s", serverError.getResponse().getStatus(), response));
|
throw new AssertionError(String.format("Request failed. HTTP status: %d, response: %s", serverError.getResponse().getStatus(), response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,66 +1,66 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import org.openqa.selenium.Alert;
|
import org.openqa.selenium.Alert;
|
||||||
import org.openqa.selenium.TimeoutException;
|
import org.openqa.selenium.TimeoutException;
|
||||||
import org.openqa.selenium.support.ui.FluentWait;
|
import org.openqa.selenium.support.ui.FluentWait;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
|
import static org.openqa.selenium.support.ui.ExpectedConditions.alertIsPresent;
|
||||||
import static org.openqa.selenium.support.ui.ExpectedConditions.not;
|
import static org.openqa.selenium.support.ui.ExpectedConditions.not;
|
||||||
|
|
||||||
public class Alerts {
|
public class Alerts {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Alerts(SeleniumWebConnector connector) {
|
public Alerts(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dismissAlert(int timeout) {
|
public void dismissAlert(int timeout) {
|
||||||
handleAlert(timeout, Alert::dismiss);
|
handleAlert(timeout, Alert::dismiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptAlert(int timeout) {
|
public void acceptAlert(int timeout) {
|
||||||
handleAlert(timeout, Alert::accept);
|
handleAlert(timeout, Alert::accept);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlertPresent(int timeout) {
|
public boolean isAlertPresent(int timeout) {
|
||||||
try {
|
try {
|
||||||
waitForAlert(timeout);
|
waitForAlert(timeout);
|
||||||
return true;
|
return true;
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAlert(int timeout, Consumer<Alert> action) {
|
private void handleAlert(int timeout, Consumer<Alert> action) {
|
||||||
waitForAlert(timeout);
|
waitForAlert(timeout);
|
||||||
action.accept(connector.getDriver()
|
action.accept(connector.getDriver()
|
||||||
.switchTo()
|
.switchTo()
|
||||||
.alert());
|
.alert());
|
||||||
|
|
||||||
new FluentWait<>(connector.getDriver())
|
new FluentWait<>(connector.getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(timeout))
|
.withTimeout(Duration.ofSeconds(timeout))
|
||||||
.pollingEvery(Duration.ofMillis(300))
|
.pollingEvery(Duration.ofMillis(300))
|
||||||
.withMessage("Alert is still present after " + timeout + " seconds timeout.")
|
.withMessage("Alert is still present after " + timeout + " seconds timeout.")
|
||||||
.until(driver -> not(alertIsPresent())).apply(connector.getDriver());
|
.until(driver -> not(alertIsPresent())).apply(connector.getDriver());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAlertText(int timeout) {
|
public String getAlertText(int timeout) {
|
||||||
waitForAlert(timeout);
|
waitForAlert(timeout);
|
||||||
return connector.getDriver()
|
return connector.getDriver()
|
||||||
.switchTo()
|
.switchTo()
|
||||||
.alert()
|
.alert()
|
||||||
.getText();
|
.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForAlert(int timeout) {
|
private void waitForAlert(int timeout) {
|
||||||
new FluentWait<>(connector.getDriver())
|
new FluentWait<>(connector.getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(timeout))
|
.withTimeout(Duration.ofSeconds(timeout))
|
||||||
.pollingEvery(Duration.ofMillis(300))
|
.pollingEvery(Duration.ofMillis(300))
|
||||||
.withMessage("Alert is not present after " + timeout + " seconds timeout.")
|
.withMessage("Alert is not present after " + timeout + " seconds timeout.")
|
||||||
.until(driver -> alertIsPresent().apply(connector.getDriver()));
|
.until(driver -> alertIsPresent().apply(connector.getDriver()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,67 +1,67 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Clickable;
|
import cz.moneta.test.harness.support.web.Clickable;
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import org.openqa.selenium.JavascriptExecutor;
|
import org.openqa.selenium.JavascriptExecutor;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.interactions.Actions;
|
import org.openqa.selenium.interactions.Actions;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Clicks {
|
public class Clicks {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
private static final int CLICKABLE_TIMEOUT = 5;
|
private static final int CLICKABLE_TIMEOUT = 5;
|
||||||
|
|
||||||
private final static String DOUBLE_CLICK_JS = "var evt = document.createEvent('MouseEvents');"
|
private final static String DOUBLE_CLICK_JS = "var evt = document.createEvent('MouseEvents');"
|
||||||
+ "evt.initMouseEvent('dblclick',true, true, window, 10, 0, 0, 0, 0, false, false, false, false, 0,null);"
|
+ "evt.initMouseEvent('dblclick',true, true, window, 10, 0, 0, 0, 0, false, false, false, false, 0,null);"
|
||||||
+ "arguments[0].dispatchEvent(evt);";
|
+ "arguments[0].dispatchEvent(evt);";
|
||||||
|
|
||||||
private final static String JS_CLICK_SCRIPT = "console.debug('jsClick start'); " +
|
private final static String JS_CLICK_SCRIPT = "console.debug('jsClick start'); " +
|
||||||
"setTimeout(function(elem){ elem.click();}, 0, arguments[0]); " +
|
"setTimeout(function(elem){ elem.click();}, 0, arguments[0]); " +
|
||||||
"console.debug('jsClick end');";
|
"console.debug('jsClick end');";
|
||||||
|
|
||||||
public Clicks(SeleniumWebConnector connector) {
|
public Clicks(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void click(Clickable clickable, Lookup lookup) {
|
public void click(Clickable clickable, Lookup lookup) {
|
||||||
WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
|
WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
|
||||||
isClickable(element).click();
|
isClickable(element).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebElement isClickable(WebElement webElement) {
|
protected WebElement isClickable(WebElement webElement) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
|
||||||
return wait.until(ExpectedConditions.elementToBeClickable(webElement));
|
return wait.until(ExpectedConditions.elementToBeClickable(webElement));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebElement isClickable(Clickable clickable, Lookup lookup) {
|
protected WebElement isClickable(Clickable clickable, Lookup lookup) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(CLICKABLE_TIMEOUT));
|
||||||
return wait.until(ExpectedConditions.elementToBeClickable(connector.resolveLookup(lookup).apply(clickable.getPath())));
|
return wait.until(ExpectedConditions.elementToBeClickable(connector.resolveLookup(lookup).apply(clickable.getPath())));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doubleClick(Clickable clickable, Lookup lookup) {
|
public void doubleClick(Clickable clickable, Lookup lookup) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
|
WebElement element = connector.waits().waitForLazyElement(clickable.getPath(), lookup);
|
||||||
actions.doubleClick(element);
|
actions.doubleClick(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void jsDoubleClick(Clickable clickable, Lookup lookup) {
|
public void jsDoubleClick(Clickable clickable, Lookup lookup) {
|
||||||
Optional.of(connector.getDriver())
|
Optional.of(connector.getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.ifPresent(e -> e.executeScript(DOUBLE_CLICK_JS,
|
.ifPresent(e -> e.executeScript(DOUBLE_CLICK_JS,
|
||||||
connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
|
connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void jsClick(Clickable clickable, Lookup lookup) {
|
public void jsClick(Clickable clickable, Lookup lookup) {
|
||||||
Optional.of(connector.getDriver())
|
Optional.of(connector.getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.ifPresent(e -> e.executeScript(JS_CLICK_SCRIPT,
|
.ifPresent(e -> e.executeScript(JS_CLICK_SCRIPT,
|
||||||
connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
|
connector.waits().waitForLazyElement(clickable.getPath(), lookup)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,30 +1,30 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import org.openqa.selenium.Cookie;
|
import org.openqa.selenium.Cookie;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Cookies {
|
public class Cookies {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Cookies(SeleniumWebConnector connector) {
|
public Cookies(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCookie(String name) {
|
public String getCookie(String name) {
|
||||||
return Optional.ofNullable(connector.getDriver().manage().getCookieNamed(name))
|
return Optional.ofNullable(connector.getDriver().manage().getCookieNamed(name))
|
||||||
.map(Cookie::getValue)
|
.map(Cookie::getValue)
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCookie(String name, String value, String domain, String path, Date expiry, boolean isSecure, boolean isHttpOnly) {
|
public void addCookie(String name, String value, String domain, String path, Date expiry, boolean isSecure, boolean isHttpOnly) {
|
||||||
Cookie cookie = new Cookie(name, value, domain, path, expiry, isSecure, isHttpOnly);
|
Cookie cookie = new Cookie(name, value, domain, path, expiry, isSecure, isHttpOnly);
|
||||||
connector.getDriver().manage().addCookie(cookie);
|
connector.getDriver().manage().addCookie(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addCookie(Cookie cookie) {
|
public void addCookie(Cookie cookie) {
|
||||||
connector.getDriver().manage().addCookie(cookie);
|
connector.getDriver().manage().addCookie(cookie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,255 +1,255 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.selenium.ClasspathFileDetector;
|
import cz.moneta.test.harness.support.selenium.ClasspathFileDetector;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.logging.*;
|
import org.openqa.selenium.logging.*;
|
||||||
import org.openqa.selenium.manager.SeleniumManager;
|
import org.openqa.selenium.manager.SeleniumManager;
|
||||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
||||||
|
|
||||||
public class DriverUtils {
|
public class DriverUtils {
|
||||||
|
|
||||||
protected static final Set<String> LOG_TYPES = Stream.of(LogType.BROWSER, LogType.CLIENT, LogType.DRIVER,
|
protected static final Set<String> LOG_TYPES = Stream.of(LogType.BROWSER, LogType.CLIENT, LogType.DRIVER,
|
||||||
LogType.PERFORMANCE, LogType.PROFILER, LogType.SERVER).collect(Collectors.toSet());
|
LogType.PERFORMANCE, LogType.PROFILER, LogType.SERVER).collect(Collectors.toSet());
|
||||||
private static final Logger logger = LogManager.getLogger(DriverUtils.class);
|
private static final Logger logger = LogManager.getLogger(DriverUtils.class);
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public DriverUtils(SeleniumWebConnector connector) {
|
public DriverUtils(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String takeSnapshot(String snapshotFileName) {
|
public String takeSnapshot(String snapshotFileName) {
|
||||||
String snapshotsPath = connector.getStore().getConfig("selenium.snapshots.path", "target/screenshots");
|
String snapshotsPath = connector.getStore().getConfig("selenium.snapshots.path", "target/screenshots");
|
||||||
File file = Paths.get(snapshotsPath, snapshotFileName).toFile();
|
File file = Paths.get(snapshotsPath, snapshotFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.writeByteArrayToFile(file, ((TakesScreenshot) connector.getDriver()).getScreenshotAs(OutputType.BYTES));
|
FileUtils.writeByteArrayToFile(file, ((TakesScreenshot) connector.getDriver()).getScreenshotAs(OutputType.BYTES));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write snapshot " + snapshotFileName + " to " + snapshotsPath, e);
|
throw new IllegalStateException("Unable to write snapshot " + snapshotFileName + " to " + snapshotsPath, e);
|
||||||
} catch (WebDriverException e2) { // An unexpected error swallows why the test failed
|
} catch (WebDriverException e2) { // An unexpected error swallows why the test failed
|
||||||
logger.error("Error during getting snapshot: " + e2.getMessage());
|
logger.error("Error during getting snapshot: " + e2.getMessage());
|
||||||
e2.printStackTrace();
|
e2.printStackTrace();
|
||||||
}
|
}
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String captureLog(String logFileName, String logType) {
|
public String captureLog(String logFileName, String logType) {
|
||||||
String path = connector.getStore().getConfig("selenium.logs.path", "target/logs");
|
String path = connector.getStore().getConfig("selenium.logs.path", "target/logs");
|
||||||
Logs logs = connector.getDriver()
|
Logs logs = connector.getDriver()
|
||||||
.manage()
|
.manage()
|
||||||
.logs();
|
.logs();
|
||||||
|
|
||||||
LogEntries logEntriesForType = null;
|
LogEntries logEntriesForType = null;
|
||||||
try {
|
try {
|
||||||
logEntriesForType = logs.get(logType);
|
logEntriesForType = logs.get(logType);
|
||||||
} catch (Exception wde) {
|
} catch (Exception wde) {
|
||||||
logger.warn(() -> "We were not able to get log entries for log type: " + logType);
|
logger.warn(() -> "We were not able to get log entries for log type: " + logType);
|
||||||
}
|
}
|
||||||
if (logEntriesForType != null) {
|
if (logEntriesForType != null) {
|
||||||
List<LogEntry> logEntries = logEntriesForType.getAll();
|
List<LogEntry> logEntries = logEntriesForType.getAll();
|
||||||
try {
|
try {
|
||||||
File file = Paths.get(path, logFileName, ".log").toFile();
|
File file = Paths.get(path, logFileName, ".log").toFile();
|
||||||
FileUtils.writeLines(file, logEntries);
|
FileUtils.writeLines(file, logEntries);
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write log " + logFileName + " to " + path, e);
|
throw new IllegalStateException("Unable to write log " + logFileName + " to " + path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void captureDom(String domFileName) {
|
public void captureDom(String domFileName) {
|
||||||
String path = connector.getStore().getConfig("selenium.dom.path", "target/dom");
|
String path = connector.getStore().getConfig("selenium.dom.path", "target/dom");
|
||||||
File file = Paths.get(path, domFileName).toFile();
|
File file = Paths.get(path, domFileName).toFile();
|
||||||
try {
|
try {
|
||||||
FileUtils.write(file, connector.getDriver().getPageSource());
|
FileUtils.write(file, connector.getDriver().getPageSource());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to write " + domFileName + " to " + path, e);
|
throw new IllegalStateException("Unable to write " + domFileName + " to " + path, e);
|
||||||
} catch (WebDriverException e2) { // An unexpected error swallows why the test failed
|
} catch (WebDriverException e2) { // An unexpected error swallows why the test failed
|
||||||
logger.error("Error during getting DOM: " + e2.getMessage());
|
logger.error("Error during getting DOM: " + e2.getMessage());
|
||||||
e2.printStackTrace();
|
e2.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add platform/browser/driver independent capabilities.
|
* Add platform/browser/driver independent capabilities.
|
||||||
*
|
*
|
||||||
* @return DesiredCapabilities with default capabilities added
|
* @return DesiredCapabilities with default capabilities added
|
||||||
*/
|
*/
|
||||||
protected void addDefaultDesiredCapabilities(MutableCapabilities capabilities) {
|
protected void addDefaultDesiredCapabilities(MutableCapabilities capabilities) {
|
||||||
LoggingPreferences logs = new LoggingPreferences();
|
LoggingPreferences logs = new LoggingPreferences();
|
||||||
LOG_TYPES.forEach(lt -> logs.enable(lt, Level.ALL));
|
LOG_TYPES.forEach(lt -> logs.enable(lt, Level.ALL));
|
||||||
//capabilities.setCapability(CapabilityType.LOGGING_PREFS, logs); // TODO: vyřešit
|
//capabilities.setCapability(CapabilityType.LOGGING_PREFS, logs); // TODO: vyřešit
|
||||||
|
|
||||||
java.util.logging.Logger.getLogger(RemoteWebDriver.class.getName()).setLevel(Level.INFO);
|
java.util.logging.Logger.getLogger(RemoteWebDriver.class.getName()).setLevel(Level.INFO);
|
||||||
java.util.logging.Logger.getLogger(SeleniumManager.class.getName()).setLevel(Level.INFO);
|
java.util.logging.Logger.getLogger(SeleniumManager.class.getName()).setLevel(Level.INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to configure additional capabilities.
|
* Allows to configure additional capabilities.
|
||||||
*
|
*
|
||||||
* @param capabilities collection of capabilities to be added to
|
* @param capabilities collection of capabilities to be added to
|
||||||
* @param key key to use for capabilities lookup in configuration
|
* @param key key to use for capabilities lookup in configuration
|
||||||
*/
|
*/
|
||||||
protected void addAdditionalDesiredCapabilities(MutableCapabilities capabilities, String key) {
|
protected void addAdditionalDesiredCapabilities(MutableCapabilities capabilities, String key) {
|
||||||
String additionalCapabilities = connector.getStore().getConfig(key);
|
String additionalCapabilities = connector.getStore().getConfig(key);
|
||||||
if (additionalCapabilities != null) {
|
if (additionalCapabilities != null) {
|
||||||
Arrays.stream(additionalCapabilities.split(",")).forEach(
|
Arrays.stream(additionalCapabilities.split(",")).forEach(
|
||||||
(item) -> {
|
(item) -> {
|
||||||
String[] keyValue = item.split("=");
|
String[] keyValue = item.split("=");
|
||||||
String value = keyValue[1];
|
String value = keyValue[1];
|
||||||
if (!keyValue[0].equals("applicationName")) {
|
if (!keyValue[0].equals("applicationName")) {
|
||||||
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
|
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
|
||||||
capabilities.setCapability(keyValue[0], Boolean.parseBoolean(value));
|
capabilities.setCapability(keyValue[0], Boolean.parseBoolean(value));
|
||||||
} else {
|
} else {
|
||||||
capabilities.setCapability(keyValue[0], value);
|
capabilities.setCapability(keyValue[0], value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebDriver initializeRemoteWebDriver(String hubUrl) {
|
WebDriver initializeRemoteWebDriver(String hubUrl) {
|
||||||
try {
|
try {
|
||||||
// TODO: timeout připojení k browseru
|
// TODO: timeout připojení k browseru
|
||||||
// HttpClient.Factory factory = HttpClient.Factory.createDefault();
|
// HttpClient.Factory factory = HttpClient.Factory.createDefault();
|
||||||
// HttpClient.Builder builder = factory.builder()
|
// HttpClient.Builder builder = factory.builder()
|
||||||
// .connectionTimeout(Duration.ofSeconds(10));
|
// .connectionTimeout(Duration.ofSeconds(10));
|
||||||
|
|
||||||
// HttpClient.Factory clientFactory = new HttpClient.Factory() {
|
// HttpClient.Factory clientFactory = new HttpClient.Factory() {
|
||||||
// @Override
|
// @Override
|
||||||
// public HttpClient.Builder builder() {
|
// public HttpClient.Builder builder() {
|
||||||
// return new HttpClient.Builder() {
|
// return new HttpClient.Builder() {
|
||||||
// @Override
|
// @Override
|
||||||
// public HttpClient createClient(URL url) {
|
// public HttpClient createClient(URL url) {
|
||||||
// return builder.createClient(url);
|
// return builder.createClient(url);
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// @Override
|
// @Override
|
||||||
// public void cleanupIdleClients() {
|
// public void cleanupIdleClients() {
|
||||||
// factory.cleanupIdleClients();
|
// factory.cleanupIdleClients();
|
||||||
// }
|
// }
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// HttpCommandExecutor executor = new HttpCommandExecutor(new HashMap<>(), new URL(hubUrl), clientFactory);
|
// HttpCommandExecutor executor = new HttpCommandExecutor(new HashMap<>(), new URL(hubUrl), clientFactory);
|
||||||
// RemoteWebDriver remoteWebDriver = new RemoteWebDriver(executor, connector.getDesiredRemoteCapabilities());
|
// RemoteWebDriver remoteWebDriver = new RemoteWebDriver(executor, connector.getDesiredRemoteCapabilities());
|
||||||
|
|
||||||
RemoteWebDriver remoteWebDriver = new RemoteWebDriver(new URL(hubUrl), connector.getDesiredRemoteCapabilities());
|
RemoteWebDriver remoteWebDriver = new RemoteWebDriver(new URL(hubUrl), connector.getDesiredRemoteCapabilities());
|
||||||
|
|
||||||
remoteWebDriver.setFileDetector(new ClasspathFileDetector());
|
remoteWebDriver.setFileDetector(new ClasspathFileDetector());
|
||||||
String sessionId = remoteWebDriver.getSessionId().toString();
|
String sessionId = remoteWebDriver.getSessionId().toString();
|
||||||
logger.info(() -> String.format("New Selenium web driver session %s started at node %s", sessionId, getNodeUrl(hubUrl, sessionId)));
|
logger.info(() -> String.format("New Selenium web driver session %s started at node %s", sessionId, getNodeUrl(hubUrl, sessionId)));
|
||||||
return remoteWebDriver;
|
return remoteWebDriver;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(() -> "Failed to initialize remote web driver with url " + hubUrl, e);
|
logger.error(() -> "Failed to initialize remote web driver with url " + hubUrl, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNodeUrl(String hubUrl, String sessionId) {
|
private String getNodeUrl(String hubUrl, String sessionId) {
|
||||||
HttpURLConnection gridApiConnection = null;
|
HttpURLConnection gridApiConnection = null;
|
||||||
try {
|
try {
|
||||||
URL url = new URL(hubUrl);
|
URL url = new URL(hubUrl);
|
||||||
URL gridGraphQLUrl = new URL(String.format("http://%s:%d/graphql", url.getHost(), url.getPort()));
|
URL gridGraphQLUrl = new URL(String.format("http://%s:%d/graphql", url.getHost(), url.getPort()));
|
||||||
Map<String, Object> headers = new HashMap<>();
|
Map<String, Object> headers = new HashMap<>();
|
||||||
headers.put("Content-Type", "application/json");
|
headers.put("Content-Type", "application/json");
|
||||||
Pair<Integer, String> response = new SimpleRestConnector(gridGraphQLUrl.toString(), "SeleniumGraphQL")
|
Pair<Integer, String> response = new SimpleRestConnector(gridGraphQLUrl.toString(), "SeleniumGraphQL")
|
||||||
.post("",
|
.post("",
|
||||||
Entity.json("{\"query\":\"{ session (id: \\\"" + sessionId + "\\\") { uri, nodeUri } } \"}"),
|
Entity.json("{\"query\":\"{ session (id: \\\"" + sessionId + "\\\") { uri, nodeUri } } \"}"),
|
||||||
new GenericType<>(String.class),
|
new GenericType<>(String.class),
|
||||||
headers);
|
headers);
|
||||||
JsonObject gridResponse = JsonParser.parseString(response.getRight().toString()).getAsJsonObject();
|
JsonObject gridResponse = JsonParser.parseString(response.getRight().toString()).getAsJsonObject();
|
||||||
String nodeUrlString = gridResponse.getAsJsonObject("data").getAsJsonObject("session").get("uri").getAsString();
|
String nodeUrlString = gridResponse.getAsJsonObject("data").getAsJsonObject("session").get("uri").getAsString();
|
||||||
URL nodeUrl = new URL(nodeUrlString);
|
URL nodeUrl = new URL(nodeUrlString);
|
||||||
this.connector.setNodeHost(nodeUrl.getHost());
|
this.connector.setNodeHost(nodeUrl.getHost());
|
||||||
return nodeUrlString;
|
return nodeUrlString;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HarnessException(String.format("Failed to get node info for session %s", sessionId), e);
|
throw new HarnessException(String.format("Failed to get node info for session %s", sessionId), e);
|
||||||
} finally {
|
} finally {
|
||||||
if (gridApiConnection != null) {
|
if (gridApiConnection != null) {
|
||||||
try {
|
try {
|
||||||
gridApiConnection.disconnect();
|
gridApiConnection.disconnect();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HarnessException("Could not close connection to grid API.", e);
|
throw new HarnessException("Could not close connection to grid API.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkIfFileExists(String directory, String filename, int waitInSeconds, boolean deleteFile, boolean useDirectoryListMethod) {
|
public boolean checkIfFileExists(String directory, String filename, int waitInSeconds, boolean deleteFile, boolean useDirectoryListMethod) {
|
||||||
boolean fileExists = false;
|
boolean fileExists = false;
|
||||||
// If no directory set as input parameter then get it from config file or System properties.
|
// If no directory set as input parameter then get it from config file or System properties.
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
directory = Stream.of(Optional.ofNullable(System.getProperty(SELENIUM_DOWNLOAD_DIRECTORY)),
|
directory = Stream.of(Optional.ofNullable(System.getProperty(SELENIUM_DOWNLOAD_DIRECTORY)),
|
||||||
Optional.ofNullable(connector.getStore().getConfig(SELENIUM_DOWNLOAD_DIRECTORY)))
|
Optional.ofNullable(connector.getStore().getConfig(SELENIUM_DOWNLOAD_DIRECTORY)))
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new HarnessConfigurationException(("You need to set download directory or configure " + SELENIUM_DOWNLOAD_DIRECTORY + " parameter!")));
|
.orElseThrow(() -> new HarnessConfigurationException(("You need to set download directory or configure " + SELENIUM_DOWNLOAD_DIRECTORY + " parameter!")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// File which we try to find
|
// File which we try to find
|
||||||
File fileToCheck = FileUtils.getFile(directory, filename);
|
File fileToCheck = FileUtils.getFile(directory, filename);
|
||||||
File folderToCheck = new File(directory);
|
File folderToCheck = new File(directory);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Checking if file is downloaded. Try it every second to "waitInSeconds" limit.
|
// Checking if file is downloaded. Try it every second to "waitInSeconds" limit.
|
||||||
for (int i = 0; i <= waitInSeconds; i++) {
|
for (int i = 0; i <= waitInSeconds; i++) {
|
||||||
// Simulating wait for file download.
|
// Simulating wait for file download.
|
||||||
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
|
||||||
|
|
||||||
if (useDirectoryListMethod) {
|
if (useDirectoryListMethod) {
|
||||||
logger.debug("Checking if file: {} present in directory list: {}.", filename, directory);
|
logger.debug("Checking if file: {} present in directory list: {}.", filename, directory);
|
||||||
fileExists = Arrays.stream(folderToCheck.list()).anyMatch(filename::equals);
|
fileExists = Arrays.stream(folderToCheck.list()).anyMatch(filename::equals);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Checking if file: {} exists in directory: {}.", filename, directory);
|
logger.debug("Checking if file: {} exists in directory: {}.", filename, directory);
|
||||||
fileExists = (fileToCheck.exists() || fileToCheck.isFile());
|
fileExists = (fileToCheck.exists() || fileToCheck.isFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileExists) break;
|
if (fileExists) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When file exists - log it and delete file
|
// When file exists - log it and delete file
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
logger.debug("Found file: {}.", fileToCheck.getAbsolutePath());
|
logger.debug("Found file: {}.", fileToCheck.getAbsolutePath());
|
||||||
logger.info("Found expected file: {} in directory {}.", filename, directory);
|
logger.info("Found expected file: {} in directory {}.", filename, directory);
|
||||||
// For cleaning purposes. (Does not delete all files, ex. in case of wrong filename the downloaded file will remain in directory.)
|
// For cleaning purposes. (Does not delete all files, ex. in case of wrong filename the downloaded file will remain in directory.)
|
||||||
if (deleteFile) {
|
if (deleteFile) {
|
||||||
logger.debug("Deleting file: {}.", fileToCheck.getAbsolutePath());
|
logger.debug("Deleting file: {}.", fileToCheck.getAbsolutePath());
|
||||||
Files.delete(fileToCheck.toPath());
|
Files.delete(fileToCheck.toPath());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new HarnessException(String.format("Could not find expected file: %s in directory %s withing the specified timeout", filename, directory));
|
throw new HarnessException(String.format("Could not find expected file: %s in directory %s withing the specified timeout", filename, directory));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new HarnessException(String.format("Something went wrong: Could not find expected file: %s in directory %s.", filename, directory), e);
|
throw new HarnessException(String.format("Something went wrong: Could not find expected file: %s in directory %s.", filename, directory), e);
|
||||||
}
|
}
|
||||||
return fileExists;
|
return fileExists;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,99 +1,99 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import cz.moneta.test.harness.support.web.Until;
|
import cz.moneta.test.harness.support.web.Until;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.openqa.selenium.JavascriptExecutor;
|
import org.openqa.selenium.JavascriptExecutor;
|
||||||
import org.openqa.selenium.TimeoutException;
|
import org.openqa.selenium.TimeoutException;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.interactions.Actions;
|
import org.openqa.selenium.interactions.Actions;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ElementsActions {
|
public class ElementsActions {
|
||||||
|
|
||||||
private static final int DEFAULT_TIMEOUT_FOR_WAIT = 30;
|
private static final int DEFAULT_TIMEOUT_FOR_WAIT = 30;
|
||||||
private static final Logger logger = LogManager.getLogger(ElementsActions.class);
|
private static final Logger logger = LogManager.getLogger(ElementsActions.class);
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
private final String JS = "var evObj = document.createEvent('MouseEvents');"
|
private final String JS = "var evObj = document.createEvent('MouseEvents');"
|
||||||
+ "evObj.initMouseEvent(\"mouseover\",true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);"
|
+ "evObj.initMouseEvent(\"mouseover\",true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);"
|
||||||
+ "arguments[0].dispatchEvent(evObj);";
|
+ "arguments[0].dispatchEvent(evObj);";
|
||||||
|
|
||||||
public ElementsActions(SeleniumWebConnector connector) {
|
public ElementsActions(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveToElement(String path, Lookup lookup) {
|
public void moveToElement(String path, Lookup lookup) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
actions.moveToElement(waitForLazyElement(path, lookup))
|
actions.moveToElement(waitForLazyElement(path, lookup))
|
||||||
.build()
|
.build()
|
||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveToElementAndClick(String hoverElementPath, String clickElementPath, Lookup lookup) {
|
public void moveToElementAndClick(String hoverElementPath, String clickElementPath, Lookup lookup) {
|
||||||
Optional.of(connector.getDriver())
|
Optional.of(connector.getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.ifPresent(e -> {
|
.ifPresent(e -> {
|
||||||
e.executeScript(JS, waitForLazyElement(hoverElementPath, lookup));
|
e.executeScript(JS, waitForLazyElement(hoverElementPath, lookup));
|
||||||
connector.clicks().click(() -> clickElementPath, lookup);
|
connector.clicks().click(() -> clickElementPath, lookup);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(String path, Lookup lookup) {
|
public String getText(String path, Lookup lookup) {
|
||||||
return waitForLazyElement(path, lookup).getText();
|
return waitForLazyElement(path, lookup).getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isElementVisible(String path, Lookup lookup) {
|
public boolean isElementVisible(String path, Lookup lookup) {
|
||||||
return isElementVisible(DEFAULT_TIMEOUT_FOR_WAIT, path, lookup);
|
return isElementVisible(DEFAULT_TIMEOUT_FOR_WAIT, path, lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isElementVisible(int timeout, String path, Lookup lookup) {
|
public boolean isElementVisible(int timeout, String path, Lookup lookup) {
|
||||||
try {
|
try {
|
||||||
waitForElement(timeout, path, lookup, Until.VISIBLE);
|
waitForElement(timeout, path, lookup, Until.VISIBLE);
|
||||||
return true;
|
return true;
|
||||||
} catch (TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
logger.warn("Element {} is not visible after {}s.", () -> path, () -> timeout);
|
logger.warn("Element {} is not visible after {}s.", () -> path, () -> timeout);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dragAndDropElement(String source, String target, Lookup lookup) {
|
public void dragAndDropElement(String source, String target, Lookup lookup) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
actions.dragAndDrop(waitForLazyElement(source, lookup), findElement(target, lookup))
|
actions.dragAndDrop(waitForLazyElement(source, lookup), findElement(target, lookup))
|
||||||
.build()
|
.build()
|
||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dragAndDropBy(String source, int xOffset, int yOffset, Lookup lookup) {
|
public void dragAndDropBy(String source, int xOffset, int yOffset, Lookup lookup) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
actions.dragAndDropBy(waitForLazyElement(source, lookup), xOffset, yOffset)
|
actions.dragAndDropBy(waitForLazyElement(source, lookup), xOffset, yOffset)
|
||||||
.build()
|
.build()
|
||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebElement waitForLazyElement(String path, Lookup lookup) {
|
private WebElement waitForLazyElement(String path, Lookup lookup) {
|
||||||
return connector.waits().waitForLazyElement(path, lookup);
|
return connector.waits().waitForLazyElement(path, lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebElement findElement(String path, Lookup lookup) {
|
private WebElement findElement(String path, Lookup lookup) {
|
||||||
return connector.getDriver()
|
return connector.getDriver()
|
||||||
.findElement(connector.resolveLookup(lookup).apply(path));
|
.findElement(connector.resolveLookup(lookup).apply(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForElement(int timeout, String path, Lookup lookup, Until until) {
|
private void waitForElement(int timeout, String path, Lookup lookup, Until until) {
|
||||||
connector.waits().waitForElements(timeout, lookup, until, path);
|
connector.waits().waitForElements(timeout, lookup, until, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollIntoView(String path, Lookup lookup) {
|
public void scrollIntoView(String path, Lookup lookup) {
|
||||||
WebElement element = connector.getDriver().findElement(connector.resolveLookup(lookup).apply(path));
|
WebElement element = connector.getDriver().findElement(connector.resolveLookup(lookup).apply(path));
|
||||||
connector.scrolling().scrollIntoView(element);
|
connector.scrolling().scrollIntoView(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WebElement> findElements(String path, Lookup lookup) {
|
public List<WebElement> findElements(String path, Lookup lookup) {
|
||||||
return connector.getDriver().findElements(connector.resolveLookup(lookup).apply(path));
|
return connector.getDriver().findElements(connector.resolveLookup(lookup).apply(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,37 +1,37 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ElementsChecks {
|
public class ElementsChecks {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public ElementsChecks(SeleniumWebConnector connector) {
|
public ElementsChecks(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isElementPresent(String path, Lookup lookup) {
|
public boolean isElementPresent(String path, Lookup lookup) {
|
||||||
return !connector.getDriver().findElements(connector.resolveLookup(lookup).apply(path)).isEmpty();
|
return !connector.getDriver().findElements(connector.resolveLookup(lookup).apply(path)).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isElementEnabled(String path, Lookup lookup) {
|
public boolean isElementEnabled(String path, Lookup lookup) {
|
||||||
return connector.waits().waitForLazyElement(path, lookup).isEnabled();
|
return connector.waits().waitForLazyElement(path, lookup).isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void isAtLeastOneElementPresent(String xpath, Lookup lookup) {
|
public void isAtLeastOneElementPresent(String xpath, Lookup lookup) {
|
||||||
connector.waits()
|
connector.waits()
|
||||||
.waitForLazyElement(xpath, lookup);
|
.waitForLazyElement(xpath, lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementContent(String path, String content, Lookup lookup) {
|
public void checkElementContent(String path, String content, Lookup lookup) {
|
||||||
List<WebElement> elements = connector.waits().waitForLazyElements(path, lookup);
|
List<WebElement> elements = connector.waits().waitForLazyElements(path, lookup);
|
||||||
elements.stream()
|
elements.stream()
|
||||||
.map(WebElement::getText)
|
.map(WebElement::getText)
|
||||||
.filter(text -> text.contains(content))
|
.filter(text -> text.contains(content))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new AssertionError(String.format("Cannot find element that contains text %s", content)));
|
.orElseThrow(() -> new AssertionError(String.format("Cannot find element that contains text %s", content)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
|
|
||||||
public class Frames {
|
public class Frames {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Frames(SeleniumWebConnector connector) {
|
public Frames(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToFrame(String frame, Lookup lookup) {
|
public void switchToFrame(String frame, Lookup lookup) {
|
||||||
connector
|
connector
|
||||||
.getDriver()
|
.getDriver()
|
||||||
.switchTo().frame(connector.waits().waitForLazyElement(frame, lookup));
|
.switchTo().frame(connector.waits().waitForLazyElement(frame, lookup));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
public class Navigations {
|
public class Navigations {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Navigations(SeleniumWebConnector connector) {
|
public Navigations(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToDefaultContent() {
|
public void switchToDefaultContent() {
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.switchTo()
|
.switchTo()
|
||||||
.defaultContent();
|
.defaultContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateTo(String url) {
|
public void navigateTo(String url) {
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.navigate()
|
.navigate()
|
||||||
.to(url);
|
.to(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateRefresh() {
|
public void navigateRefresh() {
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.navigate()
|
.navigate()
|
||||||
.refresh();
|
.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateBack() {
|
public void navigateBack() {
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.navigate()
|
.navigate()
|
||||||
.back();
|
.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigateForward() {
|
public void navigateForward() {
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.navigate()
|
.navigate()
|
||||||
.forward();
|
.forward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import org.openqa.selenium.JavascriptExecutor;
|
import org.openqa.selenium.JavascriptExecutor;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Scripts {
|
public class Scripts {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Scripts(SeleniumWebConnector connector) {
|
public Scripts(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object executeScript(String script, Object[] args) {
|
public Object executeScript(String script, Object[] args) {
|
||||||
JavascriptExecutor executor = Optional.of(connector.getDriver())
|
JavascriptExecutor executor = Optional.of(connector.getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.orElseThrow(() -> new IllegalStateException("This driver does not support JavaScript!"));
|
.orElseThrow(() -> new IllegalStateException("This driver does not support JavaScript!"));
|
||||||
return args == null
|
return args == null
|
||||||
? executor.executeScript(script)
|
? executor.executeScript(script)
|
||||||
: executor.executeScript(script, args);
|
: executor.executeScript(script, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
public class Scrolling {
|
public class Scrolling {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Scrolling(SeleniumWebConnector connector) {
|
public Scrolling(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
public void scrollIntoView(WebElement element) {
|
public void scrollIntoView(WebElement element) {
|
||||||
connector.scripts().executeScript("arguments[0].scrollIntoView(true);", new Object[]{element});
|
connector.scripts().executeScript("arguments[0].scrollIntoView(true);", new Object[]{element});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,80 +1,80 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.ui.Select;
|
import org.openqa.selenium.support.ui.Select;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Selects {
|
public class Selects {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Selects(SeleniumWebConnector connector) {
|
public Selects(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectByVisibleText(String path, String text, Lookup lookup) {
|
public void selectByVisibleText(String path, String text, Lookup lookup) {
|
||||||
WebElement element = connector.waits()
|
WebElement element = connector.waits()
|
||||||
.waitForLazyElement(path, lookup);
|
.waitForLazyElement(path, lookup);
|
||||||
connector.scrolling().scrollIntoView(element);
|
connector.scrolling().scrollIntoView(element);
|
||||||
new Select(element).selectByVisibleText(text);
|
new Select(element).selectByVisibleText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectByValue(String path, String value, Lookup lookup) {
|
public void selectByValue(String path, String value, Lookup lookup) {
|
||||||
WebElement element = connector.waits()
|
WebElement element = connector.waits()
|
||||||
.waitForLazyElement(path, lookup);
|
.waitForLazyElement(path, lookup);
|
||||||
connector.scrolling().scrollIntoView(element);
|
connector.scrolling().scrollIntoView(element);
|
||||||
connector.elementsActions().moveToElement(path, lookup);
|
connector.elementsActions().moveToElement(path, lookup);
|
||||||
new Select(element).selectByValue(value);
|
new Select(element).selectByValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectByOptionOrder(String path, int optionOrder, Lookup lookup) {
|
public void selectByOptionOrder(String path, int optionOrder, Lookup lookup) {
|
||||||
WebElement element = connector.waits()
|
WebElement element = connector.waits()
|
||||||
.waitForLazyElement(path, lookup);
|
.waitForLazyElement(path, lookup);
|
||||||
connector.scrolling().scrollIntoView(element);
|
connector.scrolling().scrollIntoView(element);
|
||||||
new Select(element).selectByIndex(--optionOrder);
|
new Select(element).selectByIndex(--optionOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select option by text part of option value
|
* Select option by text part of option value
|
||||||
* <p>
|
* <p>
|
||||||
* In some cases is necessary to select option only by part of value in option. This method accepts path to element
|
* In some cases is necessary to select option only by part of value in option. This method accepts path to element
|
||||||
* lookup strategy value and list of text parts of options.
|
* lookup strategy value and list of text parts of options.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* In this method is created in xpath for select one value. Used strategy: create xpath with contains separated by
|
* In this method is created in xpath for select one value. Used strategy: create xpath with contains separated by
|
||||||
* OR.
|
* OR.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* Example:
|
* Example:
|
||||||
* <pre>
|
* <pre>
|
||||||
* From ["ValueA", "ValueB"] will create xpath:
|
* From ["ValueA", "ValueB"] will create xpath:
|
||||||
* "//option[contains(text(),'ValueA') or contains(text(),'ValueB')]"
|
* "//option[contains(text(),'ValueA') or contains(text(),'ValueB')]"
|
||||||
* </pre>
|
* </pre>
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param path - path to select element
|
* @param path - path to select element
|
||||||
* @param lookup - lookup strategy for select element
|
* @param lookup - lookup strategy for select element
|
||||||
* @param textOptionsParts - list of option text parts to look for
|
* @param textOptionsParts - list of option text parts to look for
|
||||||
*/
|
*/
|
||||||
public void selectByContainingTexts(String path, Lookup lookup, List<String> textOptionsParts) {
|
public void selectByContainingTexts(String path, Lookup lookup, List<String> textOptionsParts) {
|
||||||
List<String> optionXpaths = textOptionsParts.stream()
|
List<String> optionXpaths = textOptionsParts.stream()
|
||||||
.map(text -> String.format("contains(text(),'%s')", text))
|
.map(text -> String.format("contains(text(),'%s')", text))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
String joinedContains = String.join(" or ", optionXpaths);
|
String joinedContains = String.join(" or ", optionXpaths);
|
||||||
String optionXpath = String.format("//option[%s]", joinedContains);
|
String optionXpath = String.format("//option[%s]", joinedContains);
|
||||||
|
|
||||||
Waits waits = connector.waits();
|
Waits waits = connector.waits();
|
||||||
waits.waitForLazyElement(path, lookup)
|
waits.waitForLazyElement(path, lookup)
|
||||||
.findElements(waits.byLocator(Lookup.XPATH, optionXpath))
|
.findElements(waits.byLocator(Lookup.XPATH, optionXpath))
|
||||||
.get(0)
|
.get(0)
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFirstSelectedOptionText(String path, Lookup lookup) {
|
public String getFirstSelectedOptionText(String path, Lookup lookup) {
|
||||||
Select select = new Select(connector.waits().waitForLazyElement(path, lookup));
|
Select select = new Select(connector.waits().waitForLazyElement(path, lookup));
|
||||||
return select.getFirstSelectedOption().getText();
|
return select.getFirstSelectedOption().getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,92 +1,92 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.Capabilities;
|
import org.openqa.selenium.Capabilities;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
import org.openqa.selenium.chrome.ChromeDriver;
|
import org.openqa.selenium.chrome.ChromeDriver;
|
||||||
import org.openqa.selenium.chrome.ChromeOptions;
|
import org.openqa.selenium.chrome.ChromeOptions;
|
||||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
|
||||||
|
|
||||||
public class SeleniumChromeConnector extends SeleniumWebConnector {
|
public class SeleniumChromeConnector extends SeleniumWebConnector {
|
||||||
|
|
||||||
public SeleniumChromeConnector(Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
|
public SeleniumChromeConnector(Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
|
||||||
super(defaultLookup, store, extraOptions);
|
super(defaultLookup, store, extraOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pair<WebDriver, Boolean> getLocalWebDriver() {
|
protected Pair<WebDriver, Boolean> getLocalWebDriver() {
|
||||||
setupDriver();
|
setupDriver();
|
||||||
|
|
||||||
return Optional.ofNullable(store.getConfig("selenium.chromebinary.path"))
|
return Optional.ofNullable(store.getConfig("selenium.chromebinary.path"))
|
||||||
.map(bin -> {
|
.map(bin -> {
|
||||||
ChromeOptions chromeOptions = getChromeOptions();
|
ChromeOptions chromeOptions = getChromeOptions();
|
||||||
chromeOptions.setBinary(bin);
|
chromeOptions.setBinary(bin);
|
||||||
return chromeOptions;
|
return chromeOptions;
|
||||||
})
|
})
|
||||||
.map(o -> Pair.<WebDriver, Boolean>of(new ChromeDriver(o), false))
|
.map(o -> Pair.<WebDriver, Boolean>of(new ChromeDriver(o), false))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDriver() {
|
private void setupDriver() {
|
||||||
Optional.ofNullable(store.getConfig("selenium.chrome.webdriver.path"))
|
Optional.ofNullable(store.getConfig("selenium.chrome.webdriver.path"))
|
||||||
.map(path -> {
|
.map(path -> {
|
||||||
System.setProperty("webdriver.chrome.driver", path);
|
System.setProperty("webdriver.chrome.driver", path);
|
||||||
return path;
|
return path;
|
||||||
})
|
})
|
||||||
.orElseThrow(() -> new AssertionError("Please provide path to the Chrome driver using selenium.chrome.webdriver.path configuration key or use a selenium grid"));
|
.orElseThrow(() -> new AssertionError("Please provide path to the Chrome driver using selenium.chrome.webdriver.path configuration key or use a selenium grid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChromeOptions getChromeOptions() {
|
private ChromeOptions getChromeOptions() {
|
||||||
ChromeOptions chromeOptions = new ChromeOptions();
|
ChromeOptions chromeOptions = new ChromeOptions();
|
||||||
|
|
||||||
Optional.ofNullable(store.getConfig("selenium.webdriver.chrome.options"))
|
Optional.ofNullable(store.getConfig("selenium.webdriver.chrome.options"))
|
||||||
.map(o -> o.split("\\s+"))
|
.map(o -> o.split("\\s+"))
|
||||||
.ifPresent(chromeOptions::addArguments);
|
.ifPresent(chromeOptions::addArguments);
|
||||||
|
|
||||||
chromeOptions.addArguments(extraOptions);
|
chromeOptions.addArguments(extraOptions);
|
||||||
|
|
||||||
this.driverUtils().addDefaultDesiredCapabilities(chromeOptions);
|
this.driverUtils().addDefaultDesiredCapabilities(chromeOptions);
|
||||||
|
|
||||||
this.driverUtils().addAdditionalDesiredCapabilities(chromeOptions, "selenium.chrome.capabilities");
|
this.driverUtils().addAdditionalDesiredCapabilities(chromeOptions, "selenium.chrome.capabilities");
|
||||||
|
|
||||||
// Set up Selenium Grid session name
|
// Set up Selenium Grid session name
|
||||||
chromeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
|
chromeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
|
||||||
|
|
||||||
// Choose which Selenium Grid node will be used
|
// Choose which Selenium Grid node will be used
|
||||||
Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
|
Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
|
||||||
.ifPresent(platform -> chromeOptions.setPlatformName(platform));
|
.ifPresent(platform -> chromeOptions.setPlatformName(platform));
|
||||||
|
|
||||||
// Overrides default download directory in Chrome profile preferences, http://chromedriver.chromium.org/capabilities
|
// Overrides default download directory in Chrome profile preferences, http://chromedriver.chromium.org/capabilities
|
||||||
// By default headless mode does not download any files unless download directory is set.
|
// By default headless mode does not download any files unless download directory is set.
|
||||||
Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
|
Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
|
||||||
.ifPresent(experimentalOptions -> {
|
.ifPresent(experimentalOptions -> {
|
||||||
Map<String, Object> prefs = new HashMap<String, Object>();
|
Map<String, Object> prefs = new HashMap<String, Object>();
|
||||||
prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
|
prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
|
||||||
chromeOptions.setExperimentalOption("prefs", prefs);
|
chromeOptions.setExperimentalOption("prefs", prefs);
|
||||||
});
|
});
|
||||||
|
|
||||||
return chromeOptions;
|
return chromeOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Capabilities getDesiredRemoteCapabilities() {
|
protected Capabilities getDesiredRemoteCapabilities() {
|
||||||
return new DesiredCapabilities(getChromeOptions());
|
return new DesiredCapabilities(getChromeOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downloadFileViaIePrompt(int waitInSeconds) {
|
public void downloadFileViaIePrompt(int waitInSeconds) {
|
||||||
throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
|
throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,91 +1,91 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.Capabilities;
|
import org.openqa.selenium.Capabilities;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
import org.openqa.selenium.edge.EdgeDriver;
|
import org.openqa.selenium.edge.EdgeDriver;
|
||||||
import org.openqa.selenium.edge.EdgeOptions;
|
import org.openqa.selenium.edge.EdgeOptions;
|
||||||
import org.openqa.selenium.remote.DesiredCapabilities;
|
import org.openqa.selenium.remote.DesiredCapabilities;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_DOWNLOAD_DIRECTORY;
|
||||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
|
import static cz.moneta.test.harness.constants.HarnessConfigConstants.SELENIUM_GRID_PLATFORM;
|
||||||
|
|
||||||
public class SeleniumEdgeConnector extends SeleniumWebConnector {
|
public class SeleniumEdgeConnector extends SeleniumWebConnector {
|
||||||
|
|
||||||
public SeleniumEdgeConnector(Function<String, By> defaultLookup, StoreAccessor store) {
|
public SeleniumEdgeConnector(Function<String, By> defaultLookup, StoreAccessor store) {
|
||||||
super(defaultLookup, store);
|
super(defaultLookup, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pair<WebDriver, Boolean> getLocalWebDriver() {
|
protected Pair<WebDriver, Boolean> getLocalWebDriver() {
|
||||||
setupDriver();
|
setupDriver();
|
||||||
|
|
||||||
EdgeDriver driver = new EdgeDriver(getEdgeOptions());
|
EdgeDriver driver = new EdgeDriver(getEdgeOptions());
|
||||||
driver.manage()
|
driver.manage()
|
||||||
.window()
|
.window()
|
||||||
.maximize();
|
.maximize();
|
||||||
return Pair.of(driver, false);
|
return Pair.of(driver, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDriver() {
|
private void setupDriver() {
|
||||||
Optional.ofNullable(store.getConfig("selenium.edge.webdriver.path"))
|
Optional.ofNullable(store.getConfig("selenium.edge.webdriver.path"))
|
||||||
.map(path -> {
|
.map(path -> {
|
||||||
System.setProperty("webdriver.edge.driver", path);
|
System.setProperty("webdriver.edge.driver", path);
|
||||||
return path;
|
return path;
|
||||||
})
|
})
|
||||||
.orElseThrow(() -> new AssertionError("Please provide path to the Edge driver using selenium.edge.webdriver.path configuration key or use a selenium grid"));
|
.orElseThrow(() -> new AssertionError("Please provide path to the Edge driver using selenium.edge.webdriver.path configuration key or use a selenium grid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private EdgeOptions getEdgeOptions() {
|
private EdgeOptions getEdgeOptions() {
|
||||||
EdgeOptions edgeOptions = new EdgeOptions();
|
EdgeOptions edgeOptions = new EdgeOptions();
|
||||||
|
|
||||||
this.driverUtils().addDefaultDesiredCapabilities(edgeOptions);
|
this.driverUtils().addDefaultDesiredCapabilities(edgeOptions);
|
||||||
|
|
||||||
Optional.ofNullable(store.getConfig("selenium.webdriver.edge.options"))
|
Optional.ofNullable(store.getConfig("selenium.webdriver.edge.options"))
|
||||||
.map(o -> o.split("\\s+"))
|
.map(o -> o.split("\\s+"))
|
||||||
.ifPresent(edgeOptions::addArguments);
|
.ifPresent(edgeOptions::addArguments);
|
||||||
edgeOptions.addArguments(extraOptions);
|
edgeOptions.addArguments(extraOptions);
|
||||||
|
|
||||||
this.driverUtils().addAdditionalDesiredCapabilities(edgeOptions, "selenium.edge.capabilities");
|
this.driverUtils().addAdditionalDesiredCapabilities(edgeOptions, "selenium.edge.capabilities");
|
||||||
|
|
||||||
// Set up Selenium Grid session name
|
// Set up Selenium Grid session name
|
||||||
edgeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
|
edgeOptions.setCapability("se:name", store.get(HarnessConfigConstants.TEST_UNIQUE_ID).toString());
|
||||||
|
|
||||||
// Choose which Selenium Grid node will be used
|
// Choose which Selenium Grid node will be used
|
||||||
Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
|
Optional.ofNullable(store.getConfig(SELENIUM_GRID_PLATFORM))
|
||||||
.ifPresent(platform -> edgeOptions.setPlatformName(platform));
|
.ifPresent(platform -> edgeOptions.setPlatformName(platform));
|
||||||
|
|
||||||
|
|
||||||
Map<String, Object> prefs = new HashMap<String, Object>();
|
Map<String, Object> prefs = new HashMap<String, Object>();
|
||||||
// Allow clipboard usage
|
// Allow clipboard usage
|
||||||
prefs.put("profile.default_content_setting_values.clipboard", 1);
|
prefs.put("profile.default_content_setting_values.clipboard", 1);
|
||||||
|
|
||||||
// Overrides default download directory in Edge profile preferences
|
// Overrides default download directory in Edge profile preferences
|
||||||
Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
|
Optional.ofNullable(store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY))
|
||||||
.ifPresent(experimentalOptions -> {
|
.ifPresent(experimentalOptions -> {
|
||||||
prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
|
prefs.put("download.default_directory", store.getConfig(SELENIUM_DOWNLOAD_DIRECTORY));
|
||||||
});
|
});
|
||||||
edgeOptions.setExperimentalOption("prefs", prefs);
|
edgeOptions.setExperimentalOption("prefs", prefs);
|
||||||
|
|
||||||
return edgeOptions;
|
return edgeOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Capabilities getDesiredRemoteCapabilities() {
|
protected Capabilities getDesiredRemoteCapabilities() {
|
||||||
return new DesiredCapabilities(getEdgeOptions());
|
return new DesiredCapabilities(getEdgeOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downloadFileViaIePrompt(int waitInSeconds) {
|
public void downloadFileViaIePrompt(int waitInSeconds) {
|
||||||
throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
|
throw new HarnessException("This method is not implemented on Chrome. Use capabilities settings instead.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,185 +1,185 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.Connector;
|
import cz.moneta.test.harness.connectors.Connector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
import org.openqa.selenium.Capabilities;
|
import org.openqa.selenium.Capabilities;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class SeleniumWebConnector implements Connector {
|
public abstract class SeleniumWebConnector implements Connector {
|
||||||
|
|
||||||
private final WebDriver webDriver;
|
private final WebDriver webDriver;
|
||||||
private final boolean isRemoteDriver;
|
private final boolean isRemoteDriver;
|
||||||
protected final Function<String, By> defaultLookup;
|
protected final Function<String, By> defaultLookup;
|
||||||
protected final StoreAccessor store;
|
protected final StoreAccessor store;
|
||||||
protected final String[] extraOptions;
|
protected final String[] extraOptions;
|
||||||
|
|
||||||
private String nodeHost;
|
private String nodeHost;
|
||||||
|
|
||||||
public SeleniumWebConnector(Function<String, By> defaultLookup, StoreAccessor store, String[] extraOptions) {
|
public SeleniumWebConnector(Function<String, By> defaultLookup, StoreAccessor store, String[] extraOptions) {
|
||||||
this.extraOptions = extraOptions;
|
this.extraOptions = extraOptions;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.defaultLookup = defaultLookup;
|
this.defaultLookup = defaultLookup;
|
||||||
Pair<WebDriver, Boolean> driverInfo = getPreferredWebDriver();
|
Pair<WebDriver, Boolean> driverInfo = getPreferredWebDriver();
|
||||||
isRemoteDriver = driverInfo.getRight();
|
isRemoteDriver = driverInfo.getRight();
|
||||||
WebDriver driver = driverInfo.getLeft();
|
WebDriver driver = driverInfo.getLeft();
|
||||||
if (driver == null) {
|
if (driver == null) {
|
||||||
throw new HarnessException("We were not able to initialize webDriver. Please check your configs, logs and talk to your admin");
|
throw new HarnessException("We were not able to initialize webDriver. Please check your configs, logs and talk to your admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO fix this - it slows down test execution but some tests are build with this implicit wait
|
//TODO fix this - it slows down test execution but some tests are build with this implicit wait
|
||||||
driver.manage().timeouts().implicitlyWait(2L, TimeUnit.SECONDS);
|
driver.manage().timeouts().implicitlyWait(2L, TimeUnit.SECONDS);
|
||||||
driver.manage().timeouts().pageLoadTimeout(90L, TimeUnit.SECONDS);
|
driver.manage().timeouts().pageLoadTimeout(90L, TimeUnit.SECONDS);
|
||||||
this.webDriver = driver;
|
this.webDriver = driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SeleniumWebConnector(Function<String, By> defaultLookup, StoreAccessor store) {
|
public SeleniumWebConnector(Function<String, By> defaultLookup, StoreAccessor store) {
|
||||||
this(defaultLookup, store, new String[]{});
|
this(defaultLookup, store, new String[]{});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Clicks clicks() {
|
public Clicks clicks() {
|
||||||
return new Clicks(this);
|
return new Clicks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Waits waits() {
|
public Waits waits() {
|
||||||
return new Waits(this);
|
return new Waits(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Alerts alerts() {
|
public Alerts alerts() {
|
||||||
return new Alerts(this);
|
return new Alerts(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Types types() {
|
public Types types() {
|
||||||
return new Types(this);
|
return new Types(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Selects selects() {
|
public Selects selects() {
|
||||||
return new Selects(this);
|
return new Selects(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SendKeys sendKeys() {
|
public SendKeys sendKeys() {
|
||||||
return new SendKeys(this);
|
return new SendKeys(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Frames frames() {
|
public Frames frames() {
|
||||||
return new Frames(this);
|
return new Frames(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Windows windows() {
|
public Windows windows() {
|
||||||
return new Windows(this);
|
return new Windows(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Navigations navigations() {
|
public Navigations navigations() {
|
||||||
return new Navigations(this);
|
return new Navigations(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ElementsChecks elementsChecks() {
|
public ElementsChecks elementsChecks() {
|
||||||
return new ElementsChecks(this);
|
return new ElementsChecks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cookies cookies() {
|
public Cookies cookies() {
|
||||||
return new Cookies(this);
|
return new Cookies(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Scripts scripts() {
|
public Scripts scripts() {
|
||||||
return new Scripts(this);
|
return new Scripts(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ElementsActions elementsActions() {
|
public ElementsActions elementsActions() {
|
||||||
return new ElementsActions(this);
|
return new ElementsActions(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Scrolling scrolling() {return new Scrolling(this); }
|
public Scrolling scrolling() {return new Scrolling(this); }
|
||||||
|
|
||||||
public DriverUtils driverUtils() {
|
public DriverUtils driverUtils() {
|
||||||
return new DriverUtils(this);
|
return new DriverUtils(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRemoteDriver() {
|
public boolean isRemoteDriver() {
|
||||||
return isRemoteDriver;
|
return isRemoteDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void downloadFileViaIePrompt(int waitInSeconds);
|
public abstract void downloadFileViaIePrompt(int waitInSeconds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a pair of web driver and whether it is a remote instance (true = remote, false = local driver)
|
* returns a pair of web driver and whether it is a remote instance (true = remote, false = local driver)
|
||||||
*/
|
*/
|
||||||
private Pair<WebDriver, Boolean> getPreferredWebDriver() {
|
private Pair<WebDriver, Boolean> getPreferredWebDriver() {
|
||||||
return Stream.<Supplier<Pair<WebDriver, Boolean>>>of(this::getDedicatedWebDriver, this::getSharedWebDriver, this::getLocalWebDriver)
|
return Stream.<Supplier<Pair<WebDriver, Boolean>>>of(this::getDedicatedWebDriver, this::getSharedWebDriver, this::getLocalWebDriver)
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalStateException("No suitable web driver could be initialized."));
|
.orElseThrow(() -> new IllegalStateException("No suitable web driver could be initialized."));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<WebDriver, Boolean> getDedicatedWebDriver() {
|
private Pair<WebDriver, Boolean> getDedicatedWebDriver() {
|
||||||
return Optional.ofNullable(store.getConfig("selenium.grid.dedicated.url"))
|
return Optional.ofNullable(store.getConfig("selenium.grid.dedicated.url"))
|
||||||
.map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
|
.map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<WebDriver, Boolean> getSharedWebDriver() {
|
private Pair<WebDriver, Boolean> getSharedWebDriver() {
|
||||||
return Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
|
return Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
|
||||||
.map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
|
.map(url -> Pair.of(driverUtils().initializeRemoteWebDriver(url), true))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAttribute(String path, Lookup lookup, String attributeName) {
|
public String getAttribute(String path, Lookup lookup, String attributeName) {
|
||||||
return waits().waitForLazyElement(path, lookup)
|
return waits().waitForLazyElement(path, lookup)
|
||||||
.getAttribute(attributeName );
|
.getAttribute(attributeName );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Pair<WebDriver, Boolean> getLocalWebDriver();
|
protected abstract Pair<WebDriver, Boolean> getLocalWebDriver();
|
||||||
|
|
||||||
protected abstract Capabilities getDesiredRemoteCapabilities();
|
protected abstract Capabilities getDesiredRemoteCapabilities();
|
||||||
|
|
||||||
public WebDriver getDriver() {
|
public WebDriver getDriver() {
|
||||||
return webDriver;
|
return webDriver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
webDriver.quit();
|
webDriver.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrentUrl() {
|
public String getCurrentUrl() {
|
||||||
return webDriver.getCurrentUrl();
|
return webDriver.getCurrentUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Function<String, By> resolveLookup(Lookup lookup) {
|
protected Function<String, By> resolveLookup(Lookup lookup) {
|
||||||
switch (lookup) {
|
switch (lookup) {
|
||||||
case XPATH:
|
case XPATH:
|
||||||
return By::xpath;
|
return By::xpath;
|
||||||
case ID:
|
case ID:
|
||||||
return By::id;
|
return By::id;
|
||||||
case CLASSNAME:
|
case CLASSNAME:
|
||||||
return By::className;
|
return By::className;
|
||||||
case NAME:
|
case NAME:
|
||||||
return By::name;
|
return By::name;
|
||||||
default:
|
default:
|
||||||
return defaultLookup;
|
return defaultLookup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNodeHost() {
|
public String getNodeHost() {
|
||||||
return nodeHost;
|
return nodeHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNodeHost(String nodeHost) {
|
public void setNodeHost(String nodeHost) {
|
||||||
this.nodeHost = nodeHost;
|
this.nodeHost = nodeHost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Key;
|
import cz.moneta.test.harness.support.web.Key;
|
||||||
import org.openqa.selenium.Keys;
|
import org.openqa.selenium.Keys;
|
||||||
import org.openqa.selenium.interactions.Actions;
|
import org.openqa.selenium.interactions.Actions;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class SendKeys {
|
public class SendKeys {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public SendKeys(SeleniumWebConnector connector) {
|
public SendKeys(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendKeysOneAtATime(Key[] keys) {
|
public void sendKeysOneAtATime(Key[] keys) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
Arrays.stream(keys)
|
Arrays.stream(keys)
|
||||||
.forEach(k -> {
|
.forEach(k -> {
|
||||||
actions.sendKeys(k).build().perform();
|
actions.sendKeys(k).build().perform();
|
||||||
try {
|
try {
|
||||||
TimeUnit.MILLISECONDS.sleep(100);
|
TimeUnit.MILLISECONDS.sleep(100);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendKeysAsChord(Key[] keys) {
|
public void sendKeysAsChord(Key[] keys) {
|
||||||
Actions actions = new Actions(connector.getDriver());
|
Actions actions = new Actions(connector.getDriver());
|
||||||
actions.sendKeys(Keys.chord(keys)).build().perform();
|
actions.sendKeys(Keys.chord(keys)).build().perform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,61 +1,61 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import cz.moneta.test.harness.support.web.TextContainer;
|
import cz.moneta.test.harness.support.web.TextContainer;
|
||||||
import org.openqa.selenium.JavascriptExecutor;
|
import org.openqa.selenium.JavascriptExecutor;
|
||||||
import org.openqa.selenium.Keys;
|
import org.openqa.selenium.Keys;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.interactions.Actions;
|
import org.openqa.selenium.interactions.Actions;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Types {
|
public class Types {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Types(SeleniumWebConnector connector) {
|
public Types(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(TextContainer input, String text, boolean clear, Lookup lookup) {
|
public void type(TextContainer input, String text, boolean clear, Lookup lookup) {
|
||||||
if (clear) {
|
if (clear) {
|
||||||
clearInput(input, lookup);
|
clearInput(input, lookup);
|
||||||
}
|
}
|
||||||
connector.waits()
|
connector.waits()
|
||||||
.waitForLazyElement(input.getPath(), lookup)
|
.waitForLazyElement(input.getPath(), lookup)
|
||||||
.sendKeys(text);
|
.sendKeys(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void jsType(TextContainer input, String text, boolean clear, Lookup lookup) {
|
public void jsType(TextContainer input, String text, boolean clear, Lookup lookup) {
|
||||||
if (clear) {
|
if (clear) {
|
||||||
clearInput(input, lookup);
|
clearInput(input, lookup);
|
||||||
}
|
}
|
||||||
Optional.of(connector.getDriver())
|
Optional.of(connector.getDriver())
|
||||||
.filter(JavascriptExecutor.class::isInstance)
|
.filter(JavascriptExecutor.class::isInstance)
|
||||||
.map(JavascriptExecutor.class::cast)
|
.map(JavascriptExecutor.class::cast)
|
||||||
.ifPresent(e -> {
|
.ifPresent(e -> {
|
||||||
WebElement element = connector.waits().waitForLazyElement(input.getPath(), lookup);
|
WebElement element = connector.waits().waitForLazyElement(input.getPath(), lookup);
|
||||||
if ("textarea".equals(element.getTagName())) {
|
if ("textarea".equals(element.getTagName())) {
|
||||||
e.executeScript("arguments[0].value=arguments[1];", element, text);
|
e.executeScript("arguments[0].value=arguments[1];", element, text);
|
||||||
} else {
|
} else {
|
||||||
e.executeScript("arguments[0].setAttribute('value', arguments[1]);", element, text);
|
e.executeScript("arguments[0].setAttribute('value', arguments[1]);", element, text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearInput(TextContainer input, Lookup lookup) {
|
private void clearInput(TextContainer input, Lookup lookup) {
|
||||||
connector.waits()
|
connector.waits()
|
||||||
.waitForLazyElement(input.getPath(), lookup)
|
.waitForLazyElement(input.getPath(), lookup)
|
||||||
.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);
|
.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void typeWithControlDown(String input, String keys, Lookup lookup) {
|
public void typeWithControlDown(String input, String keys, Lookup lookup) {
|
||||||
new Actions(connector.getDriver())
|
new Actions(connector.getDriver())
|
||||||
.click(connector.waits().waitForLazyElement(input, lookup))
|
.click(connector.waits().waitForLazyElement(input, lookup))
|
||||||
.keyDown(Keys.CONTROL)
|
.keyDown(Keys.CONTROL)
|
||||||
.sendKeys(keys)
|
.sendKeys(keys)
|
||||||
.keyUp(Keys.CONTROL)
|
.keyUp(Keys.CONTROL)
|
||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,184 +1,184 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.web.Lookup;
|
import cz.moneta.test.harness.support.web.Lookup;
|
||||||
import cz.moneta.test.harness.support.web.Until;
|
import cz.moneta.test.harness.support.web.Until;
|
||||||
import cz.moneta.test.harness.utils.expectedconditions.BooleanExpectedConditions;
|
import cz.moneta.test.harness.utils.expectedconditions.BooleanExpectedConditions;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.*;
|
||||||
import org.openqa.selenium.support.ui.ExpectedCondition;
|
import org.openqa.selenium.support.ui.ExpectedCondition;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.FluentWait;
|
import org.openqa.selenium.support.ui.FluentWait;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class Waits {
|
public class Waits {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
private static final int LAZY_ELEMENT_RENDER_TIMEOUT = 5;
|
private static final int LAZY_ELEMENT_RENDER_TIMEOUT = 5;
|
||||||
|
|
||||||
public Waits(SeleniumWebConnector connector) {
|
public Waits(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForElements(int timeoutSeconds, Lookup lookup, Until until, String... elementsToCheck) {
|
public void waitForElements(int timeoutSeconds, Lookup lookup, Until until, String... elementsToCheck) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
||||||
wait.ignoring(WebDriverException.class);
|
wait.ignoring(WebDriverException.class);
|
||||||
|
|
||||||
Arrays.stream(elementsToCheck)
|
Arrays.stream(elementsToCheck)
|
||||||
.forEach(path -> wait.until(resolveUntil(until).apply(byLocator(lookup, path))));
|
.forEach(path -> wait.until(resolveUntil(until).apply(byLocator(lookup, path))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForAtLeastOneElement(int timeoutSeconds, Lookup lookup, String... elementsToCheck) {
|
public void waitForAtLeastOneElement(int timeoutSeconds, Lookup lookup, String... elementsToCheck) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
||||||
|
|
||||||
ExpectedCondition[] expectedConditions = Arrays.stream(elementsToCheck)
|
ExpectedCondition[] expectedConditions = Arrays.stream(elementsToCheck)
|
||||||
.map(path ->
|
.map(path ->
|
||||||
ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path)))
|
ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path)))
|
||||||
.toArray(ExpectedCondition[]::new);
|
.toArray(ExpectedCondition[]::new);
|
||||||
wait.until(ExpectedConditions.or(expectedConditions));
|
wait.until(ExpectedConditions.or(expectedConditions));
|
||||||
}
|
}
|
||||||
|
|
||||||
WebElement waitForLazyElement(String path, Lookup lookup) {
|
WebElement waitForLazyElement(String path, Lookup lookup) {
|
||||||
doWaitForLazyElement(path, lookup);
|
doWaitForLazyElement(path, lookup);
|
||||||
return connector.getDriver()
|
return connector.getDriver()
|
||||||
.findElement(byLocator(lookup, path));
|
.findElement(byLocator(lookup, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WebElement> waitForLazyElements(String path, Lookup lookup) {
|
List<WebElement> waitForLazyElements(String path, Lookup lookup) {
|
||||||
doWaitForLazyElement(path, lookup);
|
doWaitForLazyElement(path, lookup);
|
||||||
return connector.getDriver()
|
return connector.getDriver()
|
||||||
.findElements(byLocator(lookup, path));
|
.findElements(byLocator(lookup, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doWaitForLazyElement(String path, Lookup lookup) {
|
private void doWaitForLazyElement(String path, Lookup lookup) {
|
||||||
new FluentWait<>(connector.getDriver())
|
new FluentWait<>(connector.getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(LAZY_ELEMENT_RENDER_TIMEOUT))
|
.withTimeout(Duration.ofSeconds(LAZY_ELEMENT_RENDER_TIMEOUT))
|
||||||
.pollingEvery(Duration.ofMillis(500))
|
.pollingEvery(Duration.ofMillis(500))
|
||||||
.ignoring(NoSuchElementException.class)
|
.ignoring(NoSuchElementException.class)
|
||||||
.ignoring(StaleElementReferenceException.class)
|
.ignoring(StaleElementReferenceException.class)
|
||||||
.withMessage("Element/s not found within " + LAZY_ELEMENT_RENDER_TIMEOUT
|
.withMessage("Element/s not found within " + LAZY_ELEMENT_RENDER_TIMEOUT
|
||||||
+ " seconds timeout. If this error occurs, you might want to consider using explicit @Wait annotation. " + path)
|
+ " seconds timeout. If this error occurs, you might want to consider using explicit @Wait annotation. " + path)
|
||||||
.until(driver -> !driver.findElements(byLocator(lookup, path)).isEmpty());
|
.until(driver -> !driver.findElements(byLocator(lookup, path)).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath, Lookup waitElementLookup,
|
public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath, Lookup waitElementLookup,
|
||||||
Lookup refreshElementLookup, int timeoutSeconds, int pollingEverySeconds) {
|
Lookup refreshElementLookup, int timeoutSeconds, int pollingEverySeconds) {
|
||||||
Runnable refreshFunction = () -> connector.clicks().click(() -> refreshElementPath, refreshElementLookup);
|
Runnable refreshFunction = () -> connector.clicks().click(() -> refreshElementPath, refreshElementLookup);
|
||||||
waitForElementAndRefresh(waitElementPath, waitElementLookup, refreshFunction, timeoutSeconds,
|
waitForElementAndRefresh(waitElementPath, waitElementLookup, refreshFunction, timeoutSeconds,
|
||||||
pollingEverySeconds, true);
|
pollingEverySeconds, true);
|
||||||
// waitForLazyElement(refreshElementPath, refreshElementLookup).click();
|
// waitForLazyElement(refreshElementPath, refreshElementLookup).click();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForElementAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction,
|
public void waitForElementAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction,
|
||||||
int timeoutSeconds, int pollingEverySeconds, boolean shouldBeWaitElementVisible) {
|
int timeoutSeconds, int pollingEverySeconds, boolean shouldBeWaitElementVisible) {
|
||||||
new FluentWait<>(connector.getDriver())
|
new FluentWait<>(connector.getDriver())
|
||||||
.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
||||||
.pollingEvery(Duration.ofSeconds(pollingEverySeconds))
|
.pollingEvery(Duration.ofSeconds(pollingEverySeconds))
|
||||||
.ignoring(NoSuchElementException.class)
|
.ignoring(NoSuchElementException.class)
|
||||||
.ignoring(StaleElementReferenceException.class)
|
.ignoring(StaleElementReferenceException.class)
|
||||||
.ignoring(TimeoutException.class)
|
.ignoring(TimeoutException.class)
|
||||||
.withMessage("Element/s not present within " + timeoutSeconds + " seconds timeout. Wait and refreshed " +
|
.withMessage("Element/s not present within " + timeoutSeconds + " seconds timeout. Wait and refreshed " +
|
||||||
"every: " + pollingEverySeconds + " seconds.\n For element: " + waitElementPath)
|
"every: " + pollingEverySeconds + " seconds.\n For element: " + waitElementPath)
|
||||||
.until(driver -> {
|
.until(driver -> {
|
||||||
if (shouldBeWaitElementVisible == true) {
|
if (shouldBeWaitElementVisible == true) {
|
||||||
return checkIfElementPresentAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
|
return checkIfElementPresentAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
|
||||||
} else {
|
} else {
|
||||||
return checkIfElementDisappearAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
|
return checkIfElementDisappearAndRefresh(waitElementPath, waitElementLookup, refreshFunction, driver);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebElement checkIfElementPresentAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
|
private WebElement checkIfElementPresentAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
|
||||||
Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
|
Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
|
||||||
.filter(Collection::isEmpty)
|
.filter(Collection::isEmpty)
|
||||||
.ifPresent(col -> refreshFunction.run());
|
.ifPresent(col -> refreshFunction.run());
|
||||||
return driver.findElement(byLocator(waitElementLookup, waitElementPath));
|
return driver.findElement(byLocator(waitElementLookup, waitElementPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfElementDisappearAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
|
private boolean checkIfElementDisappearAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction, WebDriver driver) {
|
||||||
Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
|
Optional.of(driver.findElements(byLocator(waitElementLookup, waitElementPath)))
|
||||||
.ifPresent(col -> refreshFunction.run());
|
.ifPresent(col -> refreshFunction.run());
|
||||||
return driver.findElements(byLocator(waitElementLookup, waitElementPath)).size() == 0;
|
return driver.findElements(byLocator(waitElementLookup, waitElementPath)).size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitOrFailOnErrorElement(int timeoutSeconds, String pathForWaiting, String pathToFail, Lookup lookup) {
|
public void waitOrFailOnErrorElement(int timeoutSeconds, String pathForWaiting, String pathToFail, Lookup lookup) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
||||||
wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
||||||
.pollingEvery(Duration.ofSeconds(3))
|
.pollingEvery(Duration.ofSeconds(3))
|
||||||
.withMessage("Element " + pathForWaiting + " or " + pathToFail + " isn't present after " + timeoutSeconds + " seconds")
|
.withMessage("Element " + pathForWaiting + " or " + pathToFail + " isn't present after " + timeoutSeconds + " seconds")
|
||||||
.until(ExpectedConditions.or(
|
.until(ExpectedConditions.or(
|
||||||
visibilityOfElementLocated(lookup, pathForWaiting),
|
visibilityOfElementLocated(lookup, pathForWaiting),
|
||||||
visibilityOfElementLocated(lookup, pathToFail)));
|
visibilityOfElementLocated(lookup, pathToFail)));
|
||||||
if (connector.elementsChecks().isElementPresent(pathToFail, lookup)) {
|
if (connector.elementsChecks().isElementPresent(pathToFail, lookup)) {
|
||||||
throw new HarnessException(pathToFail + " is present instead of " + pathForWaiting);
|
throw new HarnessException(pathToFail + " is present instead of " + pathForWaiting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for wait if all elements passed as parameter in defined state (Visible, Gone, Present in DOM) and there is not
|
* Method for wait if all elements passed as parameter in defined state (Visible, Gone, Present in DOM) and there is not
|
||||||
* visible fail element. Fail element is for example error screen.
|
* visible fail element. Fail element is for example error screen.
|
||||||
* <p>
|
* <p>
|
||||||
* Wait is implemented as fluent wait with 500ms polling and all parameters are transformed into expected conditions.
|
* Wait is implemented as fluent wait with 500ms polling and all parameters are transformed into expected conditions.
|
||||||
* These conditions are used in fluent wait.
|
* These conditions are used in fluent wait.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* In case of fail element visible is thrown FailElementDisplayedException.
|
* In case of fail element visible is thrown FailElementDisplayedException.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param timeoutSeconds - wait timeout
|
* @param timeoutSeconds - wait timeout
|
||||||
* @param lookup - define locator strategy
|
* @param lookup - define locator strategy
|
||||||
* @param until - state to check - Visible, Gone, Present in DOM
|
* @param until - state to check - Visible, Gone, Present in DOM
|
||||||
* @param xpathToFail - xpath for fail element/error screen
|
* @param xpathToFail - xpath for fail element/error screen
|
||||||
* @param elementsToCheck - elements to be wait for
|
* @param elementsToCheck - elements to be wait for
|
||||||
*/
|
*/
|
||||||
public void waitOrFailOnErrorXpath(int timeoutSeconds, Lookup lookup, Until until, String xpathToFail, String... elementsToCheck) {
|
public void waitOrFailOnErrorXpath(int timeoutSeconds, Lookup lookup, Until until, String xpathToFail, String... elementsToCheck) {
|
||||||
ExpectedCondition[] conditionsToCheck = Arrays.stream(elementsToCheck)
|
ExpectedCondition[] conditionsToCheck = Arrays.stream(elementsToCheck)
|
||||||
.map(elementToCheck -> resolveUntil(until).apply(byLocator(lookup, elementToCheck)))
|
.map(elementToCheck -> resolveUntil(until).apply(byLocator(lookup, elementToCheck)))
|
||||||
.toArray(ExpectedCondition[]::new);
|
.toArray(ExpectedCondition[]::new);
|
||||||
|
|
||||||
ExpectedCondition[] failElementNotDisplayed = new ExpectedCondition[]{BooleanExpectedConditions.failElementNotDisplayed(byLocator(Lookup.XPATH, xpathToFail))};
|
ExpectedCondition[] failElementNotDisplayed = new ExpectedCondition[]{BooleanExpectedConditions.failElementNotDisplayed(byLocator(Lookup.XPATH, xpathToFail))};
|
||||||
|
|
||||||
ExpectedCondition[] conditionsToEvaluate = ArrayUtils.addAll(failElementNotDisplayed, conditionsToCheck);
|
ExpectedCondition[] conditionsToEvaluate = ArrayUtils.addAll(failElementNotDisplayed, conditionsToCheck);
|
||||||
|
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeoutSeconds));
|
||||||
wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
wait.withTimeout(Duration.ofSeconds(timeoutSeconds))
|
||||||
.pollingEvery(Duration.ofMillis(500L))
|
.pollingEvery(Duration.ofMillis(500L))
|
||||||
.withMessage(getErrorElementsMessage(timeoutSeconds, elementsToCheck))
|
.withMessage(getErrorElementsMessage(timeoutSeconds, elementsToCheck))
|
||||||
.until(ExpectedConditions.and(conditionsToEvaluate));
|
.until(ExpectedConditions.and(conditionsToEvaluate));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getErrorElementsMessage(int timeoutSeconds, String... elements) {
|
private String getErrorElementsMessage(int timeoutSeconds, String... elements) {
|
||||||
String elementsInString = String.join(",", elements);
|
String elementsInString = String.join(",", elements);
|
||||||
return "Some of listed elements isn't present after " + timeoutSeconds + " seconds. Elements: " + elementsInString;
|
return "Some of listed elements isn't present after " + timeoutSeconds + " seconds. Elements: " + elementsInString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public By byLocator(Lookup lookup, String path) {
|
public By byLocator(Lookup lookup, String path) {
|
||||||
return connector.resolveLookup(lookup).apply(path);
|
return connector.resolveLookup(lookup).apply(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpectedCondition<WebElement> visibilityOfElementLocated(Lookup lookup, String path) {
|
private ExpectedCondition<WebElement> visibilityOfElementLocated(Lookup lookup, String path) {
|
||||||
return ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path));
|
return ExpectedConditions.visibilityOfElementLocated(byLocator(lookup, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Function<By, ExpectedCondition<?>> resolveUntil(Until until) {
|
private Function<By, ExpectedCondition<?>> resolveUntil(Until until) {
|
||||||
switch (until) {
|
switch (until) {
|
||||||
case VISIBLE:
|
case VISIBLE:
|
||||||
return ExpectedConditions::visibilityOfElementLocated;
|
return ExpectedConditions::visibilityOfElementLocated;
|
||||||
case PRESENT_IN_DOM:
|
case PRESENT_IN_DOM:
|
||||||
return ExpectedConditions::presenceOfElementLocated;
|
return ExpectedConditions::presenceOfElementLocated;
|
||||||
case GONE:
|
case GONE:
|
||||||
return ExpectedConditions::invisibilityOfElementLocated;
|
return ExpectedConditions::invisibilityOfElementLocated;
|
||||||
case CLICKABLE:
|
case CLICKABLE:
|
||||||
return ExpectedConditions::elementToBeClickable;
|
return ExpectedConditions::elementToBeClickable;
|
||||||
default:
|
default:
|
||||||
return ExpectedConditions::presenceOfElementLocated;
|
return ExpectedConditions::presenceOfElementLocated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,65 +1,65 @@
|
|||||||
package cz.moneta.test.harness.connectors.web;
|
package cz.moneta.test.harness.connectors.web;
|
||||||
|
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import org.openqa.selenium.Dimension;
|
import org.openqa.selenium.Dimension;
|
||||||
import org.openqa.selenium.NoSuchWindowException;
|
import org.openqa.selenium.NoSuchWindowException;
|
||||||
import org.openqa.selenium.WebDriver;
|
import org.openqa.selenium.WebDriver;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Windows {
|
public class Windows {
|
||||||
|
|
||||||
private final SeleniumWebConnector connector;
|
private final SeleniumWebConnector connector;
|
||||||
|
|
||||||
public Windows(SeleniumWebConnector connector) {
|
public Windows(SeleniumWebConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToOtherWindow(int timeout, int expectedNumberOfWindows) {
|
public void switchToOtherWindow(int timeout, int expectedNumberOfWindows) {
|
||||||
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeout));
|
WebDriverWait wait = new WebDriverWait(connector.getDriver(), Duration.ofSeconds(timeout));
|
||||||
wait.until(ExpectedConditions.numberOfWindowsToBe(expectedNumberOfWindows));
|
wait.until(ExpectedConditions.numberOfWindowsToBe(expectedNumberOfWindows));
|
||||||
connector.getDriver()
|
connector.getDriver()
|
||||||
.getWindowHandles()
|
.getWindowHandles()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(w -> getCurrentWindow().map(cw -> !cw.equals(w)).orElse(true))
|
.filter(w -> getCurrentWindow().map(cw -> !cw.equals(w)).orElse(true))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(otherWindow -> connector.getDriver().switchTo().window(otherWindow))
|
.map(otherWindow -> connector.getDriver().switchTo().window(otherWindow))
|
||||||
.orElseThrow(() -> new HarnessException(
|
.orElseThrow(() -> new HarnessException(
|
||||||
"Not possible to switch to other window. Only current window is present."));
|
"Not possible to switch to other window. Only current window is present."));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchToWindowByTitle(String title) {
|
public void switchToWindowByTitle(String title) {
|
||||||
for (String winHandle : connector.getDriver().getWindowHandles()) {
|
for (String winHandle : connector.getDriver().getWindowHandles()) {
|
||||||
if (connector.getDriver().switchTo().window(winHandle).getTitle().equals(title)) {
|
if (connector.getDriver().switchTo().window(winHandle).getTitle().equals(title)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new HarnessException("A browser window named \"" + title + "\" was not found");
|
throw new HarnessException("A browser window named \"" + title + "\" was not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchWindowResolution(int width, int height) {
|
public void switchWindowResolution(int width, int height) {
|
||||||
WebDriver.Window window = connector.getDriver().manage().window();
|
WebDriver.Window window = connector.getDriver().manage().window();
|
||||||
Dimension targetSize = new Dimension(width, height);
|
Dimension targetSize = new Dimension(width, height);
|
||||||
window.setSize(targetSize);
|
window.setSize(targetSize);
|
||||||
if (!targetSize.equals(window.getSize())) {
|
if (!targetSize.equals(window.getSize())) {
|
||||||
throw new IllegalStateException(String.format("Failed to switch window size to %d %d", width, height));
|
throw new IllegalStateException(String.format("Failed to switch window size to %d %d", width, height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<String> getCurrentWindow() {
|
private Optional<String> getCurrentWindow() {
|
||||||
try {
|
try {
|
||||||
return Optional.of(connector
|
return Optional.of(connector
|
||||||
.getDriver()
|
.getDriver()
|
||||||
.getWindowHandle());
|
.getWindowHandle());
|
||||||
} catch (NoSuchWindowException e) {
|
} catch (NoSuchWindowException e) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeCurrentWindow() {
|
public void closeCurrentWindow() {
|
||||||
connector.getDriver().close();
|
connector.getDriver().close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,78 +1,78 @@
|
|||||||
package cz.moneta.test.harness.connectors.wso2;
|
package cz.moneta.test.harness.connectors.wso2;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
|
import cz.moneta.test.harness.connectors.rest.BaseRestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
|
import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
|
||||||
import cz.moneta.test.harness.connectors.rest.ResponseHandler;
|
import cz.moneta.test.harness.connectors.rest.ResponseHandler;
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.context.ConfigAccessor;
|
import cz.moneta.test.harness.context.ConfigAccessor;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Client;
|
import jakarta.ws.rs.client.Client;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.client.Invocation;
|
import jakarta.ws.rs.client.Invocation;
|
||||||
import jakarta.ws.rs.client.WebTarget;
|
import jakarta.ws.rs.client.WebTarget;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public class RemoteWso2Connector extends BaseRestConnector implements RestConnector {
|
public class RemoteWso2Connector extends BaseRestConnector implements RestConnector {
|
||||||
|
|
||||||
private final WebTarget target;
|
private final WebTarget target;
|
||||||
|
|
||||||
public RemoteWso2Connector(ConfigAccessor store) {
|
public RemoteWso2Connector(ConfigAccessor store) {
|
||||||
try {
|
try {
|
||||||
Client client = createHttpClient("RemoteWso2Connector");
|
Client client = createHttpClient("RemoteWso2Connector");
|
||||||
this.target = Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
|
this.target = Optional.ofNullable(store.getConfig("selenium.grid.shared.url"))
|
||||||
.map(client::target)
|
.map(client::target)
|
||||||
.map(t -> t.path("grid/admin"))
|
.map(t -> t.path("grid/admin"))
|
||||||
.map(t -> t.path(Wso2ConnectorServlet.class.getSimpleName()))
|
.map(t -> t.path(Wso2ConnectorServlet.class.getSimpleName()))
|
||||||
.orElseThrow(() -> new IllegalStateException("Failed to initialize remote Wso2 connector."));
|
.orElseThrow(() -> new IllegalStateException("Failed to initialize remote Wso2 connector."));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException("Failed to initialize remote Wso2 connector.", e);
|
throw new IllegalStateException("Failed to initialize remote Wso2 connector.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType,
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, GenericType<T> responseType,
|
||||||
Map<String, Object> headers) {
|
Map<String, Object> headers) {
|
||||||
RemoteRestCallRequest get = new RemoteRestCallRequest(path, properties, headers);
|
RemoteRestCallRequest get = new RemoteRestCallRequest(path, properties, headers);
|
||||||
return sendAndReceive("call-get", get, responseType);
|
return sendAndReceive("call-get", get, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
RemoteRestCallRequest post = new RemoteRestCallRequest(path, request, headers);
|
RemoteRestCallRequest post = new RemoteRestCallRequest(path, request, headers);
|
||||||
return sendAndReceive("call-post", post, responseType);
|
return sendAndReceive("call-post", post, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType,
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, GenericType<T> responseType,
|
||||||
Map<String, Object> headers) {
|
Map<String, Object> headers) {
|
||||||
RemoteRestCallRequest delete = new RemoteRestCallRequest(path, properties, headers);
|
RemoteRestCallRequest delete = new RemoteRestCallRequest(path, properties, headers);
|
||||||
return sendAndReceive("call-delete", delete, responseType);
|
return sendAndReceive("call-delete", delete, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, GenericType<T> responseType, Map<String, Object> headers) {
|
||||||
RemoteRestCallRequest patch = new RemoteRestCallRequest(path, request, headers);
|
RemoteRestCallRequest patch = new RemoteRestCallRequest(path, request, headers);
|
||||||
return sendAndReceive("call-patch", patch, responseType);
|
return sendAndReceive("call-patch", patch, responseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RestConnector registerResponseHandler(
|
public RestConnector registerResponseHandler(
|
||||||
ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> responseHandler) {
|
ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> responseHandler) {
|
||||||
throw new UnsupportedOperationException("RemoteWso2Connector#registerResponseHandler not implemented");
|
throw new UnsupportedOperationException("RemoteWso2Connector#registerResponseHandler not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> Pair<Integer, T> sendAndReceive(String method, RemoteRestCallRequest request, GenericType<T> responseType) {
|
private <T> Pair<Integer, T> sendAndReceive(String method, RemoteRestCallRequest request, GenericType<T> responseType) {
|
||||||
Response response = target.path(method)
|
Response response = target.path(method)
|
||||||
.request()
|
.request()
|
||||||
.post(Entity.json(request));
|
.post(Entity.json(request));
|
||||||
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
return Pair.of(response.getStatus(), response.readEntity(responseType));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,33 @@
|
|||||||
package cz.moneta.test.harness.connectors.wso2;
|
package cz.moneta.test.harness.connectors.wso2;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.ResponseHandler;
|
import cz.moneta.test.harness.connectors.rest.ResponseHandler;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Invocation;
|
import jakarta.ws.rs.client.Invocation;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class TokenRenewalResponseHandler implements ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> {
|
public class TokenRenewalResponseHandler implements ResponseHandler<Pair<Invocation.Builder, Function<Invocation.Builder, Response>>, Response> {
|
||||||
|
|
||||||
private final Supplier<String> tokenSupplier;
|
private final Supplier<String> tokenSupplier;
|
||||||
|
|
||||||
public TokenRenewalResponseHandler(Supplier<String> tokenSupplier) {
|
public TokenRenewalResponseHandler(Supplier<String> tokenSupplier) {
|
||||||
this.tokenSupplier = tokenSupplier;
|
this.tokenSupplier = tokenSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the HTTP response status is 401 UNAUTHORIZED in which case it resends the original request with
|
* Checks whether the HTTP response status is 401 UNAUTHORIZED in which case it resends the original request with
|
||||||
* a renewed Authorization Bearer token
|
* a renewed Authorization Bearer token
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Response handle(Pair<Invocation.Builder, Function<Invocation.Builder, Response>> invocation, Response response) {
|
public Response handle(Pair<Invocation.Builder, Function<Invocation.Builder, Response>> invocation, Response response) {
|
||||||
if (response.getStatus() == HttpStatus.SC_UNAUTHORIZED) {
|
if (response.getStatus() == HttpStatus.SC_UNAUTHORIZED) {
|
||||||
Invocation.Builder builder = invocation.getLeft().header("Authorization", "Bearer " + tokenSupplier.get());
|
Invocation.Builder builder = invocation.getLeft().header("Authorization", "Bearer " + tokenSupplier.get());
|
||||||
return invocation.getRight().apply(builder);
|
return invocation.getRight().apply(builder);
|
||||||
} else {
|
} else {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,95 +1,95 @@
|
|||||||
package cz.moneta.test.harness.connectors.wso2;
|
package cz.moneta.test.harness.connectors.wso2;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||||
import cz.moneta.test.harness.connectors.common.ServletConfigAccessor;
|
import cz.moneta.test.harness.connectors.common.ServletConfigAccessor;
|
||||||
import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
|
import cz.moneta.test.harness.connectors.rest.RemoteRestCallRequest;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.ConfigAccessor;
|
import cz.moneta.test.harness.context.ConfigAccessor;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class Wso2ConnectorServlet extends HttpServlet {
|
public class Wso2ConnectorServlet extends HttpServlet {
|
||||||
|
|
||||||
private static final ObjectMapper JACKSON = new ObjectMapper()
|
private static final ObjectMapper JACKSON = new ObjectMapper()
|
||||||
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
|
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
|
||||||
.registerModules(new ParameterNamesModule(), new Jdk8Module(), new JavaTimeModule());
|
.registerModules(new ParameterNamesModule(), new Jdk8Module(), new JavaTimeModule());
|
||||||
|
|
||||||
private final ConfigAccessor store;
|
private final ConfigAccessor store;
|
||||||
private final SimpleRestConnector connector;
|
private final SimpleRestConnector connector;
|
||||||
|
|
||||||
public Wso2ConnectorServlet() {
|
public Wso2ConnectorServlet() {
|
||||||
this.store = new ServletConfigAccessor();
|
this.store = new ServletConfigAccessor();
|
||||||
this.connector = new SimpleRestConnector(
|
this.connector = new SimpleRestConnector(
|
||||||
store.getConfig("endpoints.wso2.url"), "Wso2ConnectorServlet");
|
store.getConfig("endpoints.wso2.url"), "Wso2ConnectorServlet");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||||
Optional.ofNullable(req.getPathInfo())
|
Optional.ofNullable(req.getPathInfo())
|
||||||
.flatMap(pi -> Stream.<Supplier<Optional<Supplier<Pair<Integer, String>>>>>of(
|
.flatMap(pi -> Stream.<Supplier<Optional<Supplier<Pair<Integer, String>>>>>of(
|
||||||
() -> Optional.of(pi).filter(i -> i.contains("/call-get")).map(i -> () -> callGet(req)),
|
() -> Optional.of(pi).filter(i -> i.contains("/call-get")).map(i -> () -> callGet(req)),
|
||||||
() -> Optional.of(pi).filter(i -> i.contains("/call-post")).map(i -> () -> callPost(req)))
|
() -> Optional.of(pi).filter(i -> i.contains("/call-post")).map(i -> () -> callPost(req)))
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst())
|
.findFirst())
|
||||||
.map(Supplier::get)
|
.map(Supplier::get)
|
||||||
.ifPresent(r -> mapOntoResponse(r, resp));
|
.ifPresent(r -> mapOntoResponse(r, resp));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mapOntoResponse(Pair<Integer, String> result, HttpServletResponse resp) {
|
private void mapOntoResponse(Pair<Integer, String> result, HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
resp.setContentType("application/json");
|
resp.setContentType("application/json");
|
||||||
resp.setStatus(result.getLeft());
|
resp.setStatus(result.getLeft());
|
||||||
resp.getWriter().write(result.getRight());
|
resp.getWriter().write(result.getRight());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace(); //TODO logging
|
e.printStackTrace(); //TODO logging
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Integer, String> callGet(HttpServletRequest req) {
|
private Pair<Integer, String> callGet(HttpServletRequest req) {
|
||||||
try {
|
try {
|
||||||
RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
|
RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
|
||||||
return connector.get(
|
return connector.get(
|
||||||
request.getPath(),
|
request.getPath(),
|
||||||
request.getGetProperties(),
|
request.getGetProperties(),
|
||||||
new GenericType<>(String.class),
|
new GenericType<>(String.class),
|
||||||
request.getHeaders());
|
request.getHeaders());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(); //TODO logging
|
e.printStackTrace(); //TODO logging
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Integer, String> callPost(HttpServletRequest req) {
|
private Pair<Integer, String> callPost(HttpServletRequest req) {
|
||||||
try {
|
try {
|
||||||
RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
|
RemoteRestCallRequest request = JACKSON.readValue(req.getInputStream(), RemoteRestCallRequest.class);
|
||||||
return connector.post(
|
return connector.post(
|
||||||
request.getPath(),
|
request.getPath(),
|
||||||
Entity.json(request.getRequest()),
|
Entity.json(request.getRequest()),
|
||||||
new GenericType<>(String.class),
|
new GenericType<>(String.class),
|
||||||
request.getHeaders());
|
request.getHeaders());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(); //TODO logging
|
e.printStackTrace(); //TODO logging
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
connector.close();
|
connector.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
package cz.moneta.test.harness.constants;
|
package cz.moneta.test.harness.constants;
|
||||||
|
|
||||||
public final class HarnessConfigConstants {
|
public final class HarnessConfigConstants {
|
||||||
|
|
||||||
public static final String TEST_UNIQUE_ID = "TEST_UNIQUE_ID";
|
public static final String TEST_UNIQUE_ID = "TEST_UNIQUE_ID";
|
||||||
public static final String TEST_SHORT_ID = "TEST_SHORT_ID";
|
public static final String TEST_SHORT_ID = "TEST_SHORT_ID";
|
||||||
public static final String VAULT_USERNAME_CONFIG = "vault.username";
|
public static final String VAULT_USERNAME_CONFIG = "vault.username";
|
||||||
public static final String VAULT_PASSWORD_CONFIG = "vault.password";
|
public static final String VAULT_PASSWORD_CONFIG = "vault.password";
|
||||||
public static final String VAULT_URL_CONFIG = "vault.url";
|
public static final String VAULT_URL_CONFIG = "vault.url";
|
||||||
public static final String VAULT_WSO2_KEYS_PATH = "vault.client.secrets.path";
|
public static final String VAULT_KAFKA_KEYS_CONFIG = "vault.kafka.secrets.path";
|
||||||
public static final String VAULT_CAGW_KEYS_PATH = "vault.cagw.client.secrets.path";
|
public static final String VAULT_WSO2_KEYS_PATH = "vault.client.secrets.path";
|
||||||
public static final String ENVIRONMENT_TYPE = "environment.type";
|
public static final String VAULT_CAGW_KEYS_PATH = "vault.cagw.client.secrets.path";
|
||||||
public static final String JENKINS_BUILD_URL = "jenkins.build.url";
|
public static final String ENVIRONMENT_TYPE = "environment.type";
|
||||||
public static final String SELENIUM_DOWNLOAD_DIRECTORY = "selenium.download.directory";
|
public static final String JENKINS_BUILD_URL = "jenkins.build.url";
|
||||||
public static final String SELENIUM_GRID_PLATFORM = "selenium.grid.platform";
|
public static final String SELENIUM_DOWNLOAD_DIRECTORY = "selenium.download.directory";
|
||||||
|
public static final String SELENIUM_GRID_PLATFORM = "selenium.grid.platform";
|
||||||
public static final long DEFAULT_REST_READ_TIMEOUT = 10L;
|
|
||||||
|
public static final long DEFAULT_REST_READ_TIMEOUT = 10L;
|
||||||
private HarnessConfigConstants() {
|
|
||||||
}
|
private HarnessConfigConstants() {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,204 +1,204 @@
|
|||||||
package cz.moneta.test.harness.context;
|
package cz.moneta.test.harness.context;
|
||||||
|
|
||||||
import cz.moneta.test.harness.annotations.TestContext;
|
import cz.moneta.test.harness.annotations.TestContext;
|
||||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
import cz.moneta.test.harness.exception.HarnessConfigurationException;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.data.Generator;
|
import cz.moneta.test.harness.support.data.Generator;
|
||||||
import cz.moneta.test.harness.support.data.GeneratorType;
|
import cz.moneta.test.harness.support.data.GeneratorType;
|
||||||
import cz.moneta.test.harness.support.util.Level;
|
import cz.moneta.test.harness.support.util.Level;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
import org.junit.jupiter.api.extension.ExtensionContext.Store;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static cz.moneta.test.harness.HarnessJunit5Extension.ACTIVE_ENDPOINTS;
|
import static cz.moneta.test.harness.HarnessJunit5Extension.ACTIVE_ENDPOINTS;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@TestContext
|
@TestContext
|
||||||
public abstract class BaseStoreAccessor implements StoreAccessor {
|
public abstract class BaseStoreAccessor implements StoreAccessor {
|
||||||
|
|
||||||
private static final Logger LOG = LogManager.getLogger("Harness");
|
private static final Logger LOG = LogManager.getLogger("Harness");
|
||||||
|
|
||||||
private final Store rootStore;
|
private final Store rootStore;
|
||||||
private final Store globalStore;
|
private final Store globalStore;
|
||||||
private final Store endpointStore;
|
private final Store endpointStore;
|
||||||
private final Store configStore;
|
private final Store configStore;
|
||||||
private final Store generatorsStore;
|
private final Store generatorsStore;
|
||||||
|
|
||||||
public BaseStoreAccessor(Store rootStore, Store globalStore, Store endpointStore, Store configStore, Store generatorsStore) {
|
public BaseStoreAccessor(Store rootStore, Store globalStore, Store endpointStore, Store configStore, Store generatorsStore) {
|
||||||
this.rootStore = rootStore;
|
this.rootStore = rootStore;
|
||||||
this.globalStore = globalStore;
|
this.globalStore = globalStore;
|
||||||
this.endpointStore = endpointStore;
|
this.endpointStore = endpointStore;
|
||||||
this.configStore = configStore;
|
this.configStore = configStore;
|
||||||
this.generatorsStore = generatorsStore;
|
this.generatorsStore = generatorsStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends Endpoint> E getEndpoint(Class<E> endpointClass, Object... args) {
|
public <E extends Endpoint> E getEndpoint(Class<E> endpointClass, Object... args) {
|
||||||
Set<Pair<Class<E>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
Set<Pair<Class<E>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
||||||
return endpointStore.getOrComputeIfAbsent(
|
return endpointStore.getOrComputeIfAbsent(
|
||||||
Pair.of(endpointClass, Arrays.asList(args)),
|
Pair.of(endpointClass, Arrays.asList(args)),
|
||||||
c -> {
|
c -> {
|
||||||
try {
|
try {
|
||||||
Class[] paramTypes = Stream.concat(
|
Class[] paramTypes = Stream.concat(
|
||||||
Stream.of(StoreAccessor.class),
|
Stream.of(StoreAccessor.class),
|
||||||
Arrays.stream(args).map(Object::getClass)).toArray(Class[]::new);
|
Arrays.stream(args).map(Object::getClass)).toArray(Class[]::new);
|
||||||
Constructor<E> constructor = endpointClass.getConstructor(paramTypes);
|
Constructor<E> constructor = endpointClass.getConstructor(paramTypes);
|
||||||
return constructor.newInstance(ArrayUtils.insert(0, args, this));
|
return constructor.newInstance(ArrayUtils.insert(0, args, this));
|
||||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
|
||||||
throw Stream.of(Optional.ofNullable(e.getCause()), Optional.of(e))
|
throw Stream.of(Optional.ofNullable(e.getCause()), Optional.of(e))
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(t -> new IllegalStateException("Endpoint " + endpointClass.getSimpleName() +
|
.map(t -> new IllegalStateException("Endpoint " + endpointClass.getSimpleName() +
|
||||||
" could not be instantiated: " + t.getMessage(), e)
|
" could not be instantiated: " + t.getMessage(), e)
|
||||||
)
|
)
|
||||||
.get();
|
.get();
|
||||||
} finally {
|
} finally {
|
||||||
activeEndpoints.add(c);
|
activeEndpoints.add(c);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
endpointClass);
|
endpointClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeEndpoint(Endpoint activeEndpoint) {
|
public void closeEndpoint(Endpoint activeEndpoint) {
|
||||||
Set<Pair<Class<Endpoint>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
Set<Pair<Class<Endpoint>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
||||||
Pair<Class<Endpoint>, List<Object>> pair = activeEndpoints.stream()
|
Pair<Class<Endpoint>, List<Object>> pair = activeEndpoints.stream()
|
||||||
.filter(p -> endpointStore.get(p, Endpoint.class) == activeEndpoint)
|
.filter(p -> endpointStore.get(p, Endpoint.class) == activeEndpoint)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Cannot find active endpoint to close"));
|
.orElseThrow(() -> new IllegalArgumentException("Cannot find active endpoint to close"));
|
||||||
|
|
||||||
Endpoint endpoint = endpointStore.get(pair, Endpoint.class);
|
Endpoint endpoint = endpointStore.get(pair, Endpoint.class);
|
||||||
endpoint.close();
|
endpoint.close();
|
||||||
activeEndpoints.remove(pair);
|
activeEndpoints.remove(pair);
|
||||||
endpointStore.remove(pair, Endpoint.class);
|
endpointStore.remove(pair, Endpoint.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends Endpoint> Set<E> getActiveEndpoints() {
|
public <E extends Endpoint> Set<E> getActiveEndpoints() {
|
||||||
Set<Pair<Class<E>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
Set<Pair<Class<E>, List<Object>>> activeEndpoints = endpointStore.get(ACTIVE_ENDPOINTS, Set.class);
|
||||||
return activeEndpoints.stream()
|
return activeEndpoints.stream()
|
||||||
.map(pair -> (E) endpointStore.get(pair, Endpoint.class))
|
.map(pair -> (E) endpointStore.get(pair, Endpoint.class))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfig(String key) {
|
public String getConfig(String key) {
|
||||||
return configStore.get(key, String.class);
|
return configStore.get(key, String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getConfig(String key, String defaultValue) {
|
public String getConfig(String key, String defaultValue) {
|
||||||
return Optional.ofNullable(configStore.get(key, String.class)).orElse(defaultValue);
|
return Optional.ofNullable(configStore.get(key, String.class)).orElse(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putConfig(String key, String value) {
|
public void putConfig(String key, String value) {
|
||||||
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
||||||
configStore.put(key, value);
|
configStore.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T get(String key) {
|
public <T> T get(String key) {
|
||||||
return get(key, false);
|
return get(key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T get(String key, boolean nullEnabled) {
|
public <T> T get(String key, boolean nullEnabled) {
|
||||||
if (nullEnabled) {
|
if (nullEnabled) {
|
||||||
return (T) globalStore.get(key);
|
return (T) globalStore.get(key);
|
||||||
} else {
|
} else {
|
||||||
return (T) Optional.ofNullable(globalStore.get(key))
|
return (T) Optional.ofNullable(globalStore.get(key))
|
||||||
.orElseThrow(() -> new HarnessException("Error getting value from store, key: " + key + " is null\nCheck if value in this key is correctly set"));
|
.orElseThrow(() -> new HarnessException("Error getting value from store, key: " + key + " is null\nCheck if value in this key is correctly set"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void store(String key, Object value) {
|
public void store(String key, Object value) {
|
||||||
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
||||||
globalStore.put(key, value);
|
globalStore.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeGlobal(String key, Object value) {
|
public void storeGlobal(String key, Object value) {
|
||||||
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
log(Level.INFO, "Value: " + value + " is stored with key: " + key);
|
||||||
rootStore.put(key, value);
|
rootStore.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeGlobal(String key, Supplier orCreate) {
|
public void storeGlobal(String key, Supplier orCreate) {
|
||||||
log(Level.INFO, "Value: " + orCreate.get().toString() + " is stored with key: " + key);
|
log(Level.INFO, "Value: " + orCreate.get().toString() + " is stored with key: " + key);
|
||||||
rootStore.getOrComputeIfAbsent(key, k -> orCreate.get());
|
rootStore.getOrComputeIfAbsent(key, k -> orCreate.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getGlobal(String key, Supplier<T> orCreate) {
|
public <T> T getGlobal(String key, Supplier<T> orCreate) {
|
||||||
return (T) rootStore.getOrComputeIfAbsent(key, k -> orCreate.get());
|
return (T) rootStore.getOrComputeIfAbsent(key, k -> orCreate.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T getGlobal(String key) {
|
public <T> T getGlobal(String key) {
|
||||||
return (T) rootStore.get(key);
|
return (T) rootStore.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(String template, Object... args) {
|
public void log(String template, Object... args) {
|
||||||
LOG.info(template, args);
|
LOG.info(template, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(Level level, String template, Object... args) {
|
public void log(Level level, String template, Object... args) {
|
||||||
LOG.log(org.apache.logging.log4j.Level.getLevel(level.name()), template, args);
|
LOG.log(org.apache.logging.log4j.Level.getLevel(level.name()), template, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addGenerator(GeneratorType type, Generator<?> generator) {
|
protected void addGenerator(GeneratorType type, Generator<?> generator) {
|
||||||
generatorsStore.put(type, generator);
|
generatorsStore.put(type, generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T generate(GeneratorType type, Object... params) {
|
public <T> T generate(GeneratorType type, Object... params) {
|
||||||
return (T) generatorsStore.get(type, Generator.class).generate(this, params);
|
return (T) generatorsStore.get(type, Generator.class).generate(this, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get config value from system property or value from config store.
|
* Get config value from system property or value from config store.
|
||||||
* <p>
|
* <p>
|
||||||
* Value get by priority:
|
* Value get by priority:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>
|
* <li>
|
||||||
* System property (e.g. passed by maven -D)
|
* System property (e.g. passed by maven -D)
|
||||||
* </li>
|
* </li>
|
||||||
* <li>
|
* <li>
|
||||||
* Value from config store
|
* Value from config store
|
||||||
* </li>
|
* </li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* In case of value not found HarnessConfigurationException is thrown.
|
* In case of value not found HarnessConfigurationException is thrown.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param key identifier
|
* @param key identifier
|
||||||
* @return String value for defined key
|
* @return String value for defined key
|
||||||
*/
|
*/
|
||||||
public String getSystemOrConfigValue(String key) {
|
public String getSystemOrConfigValue(String key) {
|
||||||
return Stream.of(Optional.ofNullable(System.getProperty(key)), Optional.ofNullable(getConfig(key)))
|
return Stream.of(Optional.ofNullable(System.getProperty(key)), Optional.ofNullable(getConfig(key)))
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new HarnessConfigurationException(("You need to configure " + key + " parameter!")));
|
.orElseThrow(() -> new HarnessConfigurationException(("You need to configure " + key + " parameter!")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
package cz.moneta.test.harness.context;
|
package cz.moneta.test.harness.context;
|
||||||
|
|
||||||
public interface ConfigAccessor {
|
public interface ConfigAccessor {
|
||||||
|
|
||||||
String getConfig(String key);
|
String getConfig(String key);
|
||||||
|
|
||||||
String getConfig(String key, String defaultValue);
|
String getConfig(String key, String defaultValue);
|
||||||
|
|
||||||
void putConfig(String key, String value);
|
void putConfig(String key, String value);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,98 +1,98 @@
|
|||||||
package cz.moneta.test.harness.context;
|
package cz.moneta.test.harness.context;
|
||||||
|
|
||||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
import cz.moneta.test.harness.support.data.Generator;
|
import cz.moneta.test.harness.support.data.Generator;
|
||||||
import cz.moneta.test.harness.support.data.GeneratorType;
|
import cz.moneta.test.harness.support.data.GeneratorType;
|
||||||
import cz.moneta.test.harness.support.util.Level;
|
import cz.moneta.test.harness.support.util.Level;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public interface StoreAccessor extends ConfigAccessor {
|
public interface StoreAccessor extends ConfigAccessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to get a direct access to an application or system endpoint. Typical usage is:
|
* This method is used to get a direct access to an application or system endpoint. Typical usage is:
|
||||||
* <br/>
|
* <br/>
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* public Login openLoginPage() {
|
* public Login openLoginPage() {
|
||||||
* LoansBranchEndpoint endpoint = harness.getEndpoint(LoansBranchEndpoint.class);
|
* LoansBranchEndpoint endpoint = harness.getEndpoint(LoansBranchEndpoint.class);
|
||||||
* endpoint.openApplication();
|
* endpoint.openApplication();
|
||||||
* return Builders.newWebFlowBuilder(Login.class, endpoint, harness);
|
* return Builders.newWebFlowBuilder(Login.class, endpoint, harness);
|
||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* Subsequent calls to this method within the same test class return the same instance of the endpoint.
|
* Subsequent calls to this method within the same test class return the same instance of the endpoint.
|
||||||
* <br/>
|
* <br/>
|
||||||
* <i><b>NOTE</b></i> calling this method with different arguments results in multiple instance of the same endpoint
|
* <i><b>NOTE</b></i> calling this method with different arguments results in multiple instance of the same endpoint
|
||||||
* being instantiated
|
* being instantiated
|
||||||
*
|
*
|
||||||
* @param endpointClass desired endpoint class
|
* @param endpointClass desired endpoint class
|
||||||
* @param args arguments to be passed to the endpoint implementation constructor
|
* @param args arguments to be passed to the endpoint implementation constructor
|
||||||
* @return returns an existing or - in case has not been instantiated - new instance of the endpoint
|
* @return returns an existing or - in case has not been instantiated - new instance of the endpoint
|
||||||
*/
|
*/
|
||||||
<E extends Endpoint> E getEndpoint(Class<E> endpointClass, Object... args);
|
<E extends Endpoint> E getEndpoint(Class<E> endpointClass, Object... args);
|
||||||
|
|
||||||
void closeEndpoint(Endpoint endpoint);
|
void closeEndpoint(Endpoint endpoint);
|
||||||
|
|
||||||
<E extends Endpoint> Set<E> getActiveEndpoints();
|
<E extends Endpoint> Set<E> getActiveEndpoints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a value previously stored via the {@link StoreAccessor#store} method within the same test class.
|
* Returns a value previously stored via the {@link StoreAccessor#store} method within the same test class.
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* harness.withUfoBanka()
|
* harness.withUfoBanka()
|
||||||
* .openLoginPage()
|
* .openLoginPage()
|
||||||
* .then(bankaTasks.login().withCredentialsAndTurnOffSignpad(credentials))
|
* .then(bankaTasks.login().withCredentialsAndTurnOffSignpad(credentials))
|
||||||
* .fillSearchTerm(harness.get(HKO001_MainClientPage.CLIENT_CIF_STORE_KEY))
|
* .fillSearchTerm(harness.get(HKO001_MainClientPage.CLIENT_CIF_STORE_KEY))
|
||||||
* .clickSearchByCif()
|
* .clickSearchByCif()
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
<T> T get(String key);
|
<T> T get(String key);
|
||||||
|
|
||||||
<T> T get(String key, boolean nullEnabled);
|
<T> T get(String key, boolean nullEnabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given value with the key. The key-value pair is kept for the duration of the test class execution.
|
* Stores the given value with the key. The key-value pair is kept for the duration of the test class execution.
|
||||||
*/
|
*/
|
||||||
void store(String key, Object value);
|
void store(String key, Object value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works much like {@link StoreAccessor#get} only it gets values from the persistent global storage that is kept
|
* Works much like {@link StoreAccessor#get} only it gets values from the persistent global storage that is kept
|
||||||
* for the duration of the execution of all test classes
|
* for the duration of the execution of all test classes
|
||||||
*
|
*
|
||||||
* @param key key
|
* @param key key
|
||||||
* @param orCreate if the key-value pair is not found, the supplier instantiates and stores the value before
|
* @param orCreate if the key-value pair is not found, the supplier instantiates and stores the value before
|
||||||
* returning it
|
* returning it
|
||||||
*/
|
*/
|
||||||
<T> T getGlobal(String key, Supplier<T> orCreate);
|
<T> T getGlobal(String key, Supplier<T> orCreate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works much like {@link StoreAccessor#get} only it gets values from the persistent global storage that is kept
|
* Works much like {@link StoreAccessor#get} only it gets values from the persistent global storage that is kept
|
||||||
* for the duration of the execution of all test classes
|
* for the duration of the execution of all test classes
|
||||||
*/
|
*/
|
||||||
<T> T getGlobal(String key);
|
<T> T getGlobal(String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Works much like {@link StoreAccessor#store} only it stores values in the persistent global storage that is kept
|
* Works much like {@link StoreAccessor#store} only it stores values in the persistent global storage that is kept
|
||||||
* for the duration of the execution of all test classes
|
* for the duration of the execution of all test classes
|
||||||
*/
|
*/
|
||||||
void storeGlobal(String key, Object value);
|
void storeGlobal(String key, Object value);
|
||||||
|
|
||||||
void storeGlobal(String key, Supplier orCreate);
|
void storeGlobal(String key, Supplier orCreate);
|
||||||
|
|
||||||
void log(String template, Object... args);
|
void log(String template, Object... args);
|
||||||
|
|
||||||
void log(Level level, String template, Object... args);
|
void log(Level level, String template, Object... args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a value.
|
* Generates a value.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Typical usage:
|
* Typical usage:
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* String ico = harness.generate(GeneratorType.ICO);
|
* String ico = harness.generate(GeneratorType.ICO);
|
||||||
* }</pre>
|
* }</pre>
|
||||||
|
|
||||||
* @param type typ of the generator to be used
|
* @param type typ of the generator to be used
|
||||||
* @param params parameters to be passed to the {@link Generator#generate} method
|
* @param params parameters to be passed to the {@link Generator#generate} method
|
||||||
*/
|
*/
|
||||||
<T> T generate(GeneratorType type, Object... params);
|
<T> T generate(GeneratorType type, Object... params);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
package cz.moneta.test.harness.data;
|
package cz.moneta.test.harness.data;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public enum Browser {
|
public enum Browser {
|
||||||
|
|
||||||
GOOGLE_CHROME("chrome"),
|
GOOGLE_CHROME("chrome"),
|
||||||
MS_EDGE("edge"),
|
MS_EDGE("edge"),
|
||||||
NOT_SUPPORTED("not_supported");
|
NOT_SUPPORTED("not_supported");
|
||||||
|
|
||||||
private String configName;
|
private String configName;
|
||||||
|
|
||||||
Browser(String configName) {
|
Browser(String configName) {
|
||||||
this.configName = configName;
|
this.configName = configName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getConfigName() {
|
public String getConfigName() {
|
||||||
return configName;
|
return configName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Browser getBrowserByConfigName(String configName) {
|
public static Browser getBrowserByConfigName(String configName) {
|
||||||
return Arrays.stream(values())
|
return Arrays.stream(values())
|
||||||
.filter(browser -> browser.getConfigName().equals(configName.trim().toLowerCase()))
|
.filter(browser -> browser.getConfigName().equals(configName.trim().toLowerCase()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(NOT_SUPPORTED);
|
.orElse(NOT_SUPPORTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.WsConnector;
|
import cz.moneta.test.harness.connectors.WsConnector;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
public abstract class BaseWsEndpoint implements Endpoint {
|
public abstract class BaseWsEndpoint implements Endpoint {
|
||||||
|
|
||||||
private final WsConnector connector;
|
private final WsConnector connector;
|
||||||
|
|
||||||
public BaseWsEndpoint(String address, URL wsdlUrl, QName serviceName) {
|
public BaseWsEndpoint(String address, URL wsdlUrl, QName serviceName) {
|
||||||
connector = new WsConnector(address, wsdlUrl, serviceName);
|
connector = new WsConnector(address, wsdlUrl, serviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <RESP> RESP invoke(Object request, Class<RESP> responseClass) {
|
public <RESP> RESP invoke(Object request, Class<RESP> responseClass) {
|
||||||
return connector.invoke(request, responseClass);
|
return connector.invoke(request, responseClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.annotations.Environment;
|
import cz.moneta.test.harness.annotations.Environment;
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
|
|
||||||
public class CommonEndpoint implements Endpoint {
|
public class CommonEndpoint implements Endpoint {
|
||||||
|
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
public CommonEndpoint(StoreAccessor storeAccessor) {
|
public CommonEndpoint(StoreAccessor storeAccessor) {
|
||||||
this.environment = getEnvironmentByConfigName(storeAccessor);
|
this.environment = getEnvironmentByConfigName(storeAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Environment getEnvironmentByConfigName(StoreAccessor storeAccessor) {
|
private Environment getEnvironmentByConfigName(StoreAccessor storeAccessor) {
|
||||||
String environmentConfigName = System.getProperty(HarnessConfigConstants.ENVIRONMENT_TYPE);
|
String environmentConfigName = System.getProperty(HarnessConfigConstants.ENVIRONMENT_TYPE);
|
||||||
|
|
||||||
if (environmentConfigName == null) {
|
if (environmentConfigName == null) {
|
||||||
environmentConfigName = storeAccessor.getConfig(HarnessConfigConstants.ENVIRONMENT_TYPE);
|
environmentConfigName = storeAccessor.getConfig(HarnessConfigConstants.ENVIRONMENT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Environment.fromString(environmentConfigName);
|
return Environment.fromString(environmentConfigName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Environment getEnvironment() {
|
public Environment getEnvironment() {
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endpoints are the the primary interface for interacting with the Harness library.
|
* Endpoints are the the primary interface for interacting with the Harness library.
|
||||||
* <p/>
|
* <p/>
|
||||||
* They serve as unified wrappers around implementation details (i.e. native libraries), so, that no prior knowledge
|
* They serve as unified wrappers around implementation details (i.e. native libraries), so, that no prior knowledge
|
||||||
* of numerous system client libraries is required.
|
* of numerous system client libraries is required.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Common endpoint would hold an instance of a single {@link cz.moneta.test.harness.connectors.Connector Connector}
|
* Common endpoint would hold an instance of a single {@link cz.moneta.test.harness.connectors.Connector Connector}
|
||||||
* responsible for the actual native client library interaction. Such connectors would include REST, WS, ORACLE or
|
* responsible for the actual native client library interaction. Such connectors would include REST, WS, ORACLE or
|
||||||
* Selenium ones.
|
* Selenium ones.
|
||||||
* <p/>
|
* <p/>
|
||||||
* While implementing an endpoint for a new system, it is a good idea to find a similar endpoint harnessing the
|
* While implementing an endpoint for a new system, it is a good idea to find a similar endpoint harnessing the
|
||||||
* same/required connector type. For example when implementing an endpoint for a new REST API, one
|
* same/required connector type. For example when implementing an endpoint for a new REST API, one
|
||||||
* could start with modifying {@link cz.moneta.test.harness.endpoints.autoapi.AutoApiEndpoint AutoApiEndpoint}
|
* could start with modifying {@link cz.moneta.test.harness.endpoints.autoapi.AutoApiEndpoint AutoApiEndpoint}
|
||||||
* or similar
|
* or similar
|
||||||
* <p/>
|
* <p/>
|
||||||
* Some endpoints may require a new Connector implemented for a specific technology which is not handled by Harness yet
|
* Some endpoints may require a new Connector implemented for a specific technology which is not handled by Harness yet
|
||||||
* <p/>
|
* <p/>
|
||||||
* Endpoints are usually instantiated inside a {@code harness.withSomeSystemName()} method which in turn calls
|
* Endpoints are usually instantiated inside a {@code harness.withSomeSystemName()} method which in turn calls
|
||||||
* {@code harness.getEndpoint(SomeSystemEndpoint.class)}
|
* {@code harness.getEndpoint(SomeSystemEndpoint.class)}
|
||||||
* <p/>
|
* <p/>
|
||||||
* All endpoints instantiated via the {@link cz.moneta.test.harness.context.StoreAccessor#getEndpoint(Class, Object...)}
|
* All endpoints instantiated via the {@link cz.moneta.test.harness.context.StoreAccessor#getEndpoint(Class, Object...)}
|
||||||
* method are automatically closed (i.e. the resources are released) after each test
|
* method are automatically closed (i.e. the resources are released) after each test
|
||||||
*/
|
*/
|
||||||
public interface Endpoint {
|
public interface Endpoint {
|
||||||
|
|
||||||
default boolean canAccess() {
|
default boolean canAccess() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void close() {
|
default void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,190 +1,190 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
import cz.moneta.test.harness.connectors.mobile.AppiumMobileConnector;
|
||||||
import cz.moneta.test.harness.connectors.mobile.UnsupportedPlatformException;
|
import cz.moneta.test.harness.connectors.mobile.UnsupportedPlatformException;
|
||||||
import cz.moneta.test.harness.connectors.mobile.android.AndroidConnector;
|
import cz.moneta.test.harness.connectors.mobile.android.AndroidConnector;
|
||||||
import cz.moneta.test.harness.connectors.mobile.ios.IosConnector;
|
import cz.moneta.test.harness.connectors.mobile.ios.IosConnector;
|
||||||
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
import cz.moneta.test.harness.endpoints.smartbanka.MobilePlatform;
|
||||||
import cz.moneta.test.harness.exception.HarnessException;
|
import cz.moneta.test.harness.exception.HarnessException;
|
||||||
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
import cz.moneta.test.harness.support.mobile.MobileLookup;
|
||||||
import cz.moneta.test.harness.support.mobile.share.Direction;
|
import cz.moneta.test.harness.support.mobile.share.Direction;
|
||||||
import cz.moneta.test.harness.support.web.Clickable;
|
import cz.moneta.test.harness.support.web.Clickable;
|
||||||
import cz.moneta.test.harness.support.web.TextContainer;
|
import cz.moneta.test.harness.support.web.TextContainer;
|
||||||
import cz.moneta.test.harness.support.web.Until;
|
import cz.moneta.test.harness.support.web.Until;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class MobileEndpoint<T extends AppiumMobileConnector> implements Endpoint {
|
public class MobileEndpoint<T extends AppiumMobileConnector> implements Endpoint {
|
||||||
protected final ThreadLocal<T> connector;
|
protected final ThreadLocal<T> connector;
|
||||||
|
|
||||||
public MobileEndpoint(Supplier<T> connectorSupplier) {
|
public MobileEndpoint(Supplier<T> connectorSupplier) {
|
||||||
this.connector = ThreadLocal.withInitial(connectorSupplier);
|
this.connector = ThreadLocal.withInitial(connectorSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AndroidConnector getAndroid() {
|
public AndroidConnector getAndroid() {
|
||||||
try {
|
try {
|
||||||
return (AndroidConnector) connector.get();
|
return (AndroidConnector) connector.get();
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
throw new UnsupportedPlatformException(MobilePlatform.ANDROID);
|
throw new UnsupportedPlatformException(MobilePlatform.ANDROID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IosConnector getIos() {
|
public IosConnector getIos() {
|
||||||
try {
|
try {
|
||||||
return (IosConnector) connector.get();
|
return (IosConnector) connector.get();
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
throw new UnsupportedPlatformException(MobilePlatform.IOS);
|
throw new UnsupportedPlatformException(MobilePlatform.IOS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void click(Clickable clickable, MobileLookup mobileLookup) {
|
public void click(Clickable clickable, MobileLookup mobileLookup) {
|
||||||
connector.get().click(clickable.getPath(), mobileLookup);
|
connector.get().click(clickable.getPath(), mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void acceptAlert(int timeout) {
|
public void acceptAlert(int timeout) {
|
||||||
connector.get().acceptAlert(timeout);
|
connector.get().acceptAlert(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dismissAlert(int timeout) {
|
public void dismissAlert(int timeout) {
|
||||||
connector.get().dismissAlert(timeout);
|
connector.get().dismissAlert(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(String path, MobileLookup mobileLookup) {
|
public String getText(String path, MobileLookup mobileLookup) {
|
||||||
return connector.get().getText(path, mobileLookup);
|
return connector.get().getText(path, mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText(String path) {
|
public String getText(String path) {
|
||||||
return getText(path, MobileLookup.DEFAULT);
|
return getText(path, MobileLookup.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForElementsToLoad(int timeoutSeconds, MobileLookup mobileLookup, Until until, String... elementsToCheck) {
|
public void waitForElementsToLoad(int timeoutSeconds, MobileLookup mobileLookup, Until until, String... elementsToCheck) {
|
||||||
connector.get().waitForElements(timeoutSeconds, mobileLookup, until, elementsToCheck);
|
connector.get().waitForElements(timeoutSeconds, mobileLookup, until, elementsToCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sleepSeconds(int seconds) {
|
public void sleepSeconds(int seconds) {
|
||||||
try {
|
try {
|
||||||
TimeUnit.SECONDS.sleep(seconds);
|
TimeUnit.SECONDS.sleep(seconds);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scroll(Direction direction) {
|
public void scroll(Direction direction) {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case UP:
|
case UP:
|
||||||
connector.get().scrollUp();
|
connector.get().scrollUp();
|
||||||
break;
|
break;
|
||||||
case DOWN:
|
case DOWN:
|
||||||
connector.get().scrollDown();
|
connector.get().scrollDown();
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
connector.get().scrollRight();
|
connector.get().scrollRight();
|
||||||
break;
|
break;
|
||||||
case LEFT:
|
case LEFT:
|
||||||
connector.get().scrollLeft();
|
connector.get().scrollLeft();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HarnessException("unknown direction");
|
throw new HarnessException("unknown direction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scrollToElement(Direction direction, MobileLookup mobileLookup, String path) {
|
public void scrollToElement(Direction direction, MobileLookup mobileLookup, String path) {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case UP:
|
case UP:
|
||||||
connector.get().scrollUpUntil(path, mobileLookup);
|
connector.get().scrollUpUntil(path, mobileLookup);
|
||||||
break;
|
break;
|
||||||
case DOWN:
|
case DOWN:
|
||||||
connector.get().scrollDownUntil(path, mobileLookup);
|
connector.get().scrollDownUntil(path, mobileLookup);
|
||||||
break;
|
break;
|
||||||
case LEFT:
|
case LEFT:
|
||||||
connector.get().scrollLeftUntil(path, mobileLookup);
|
connector.get().scrollLeftUntil(path, mobileLookup);
|
||||||
break;
|
break;
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
connector.get().scrollRightUntil(path, mobileLookup);
|
connector.get().scrollRightUntil(path, mobileLookup);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HarnessException("unknown direction");
|
throw new HarnessException("unknown direction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeViewLeft(){
|
public void swipeViewLeft(){
|
||||||
connector.get().swipeViewLeft();
|
connector.get().swipeViewLeft();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeViewRight(){
|
public void swipeViewRight(){
|
||||||
connector.get().swipeViewRight();
|
connector.get().swipeViewRight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipeFromToElement(String source, String target, MobileLookup mobileLookup) {
|
public void swipeFromToElement(String source, String target, MobileLookup mobileLookup) {
|
||||||
connector.get().swipeFromToElement(source, target, mobileLookup);
|
connector.get().swipeFromToElement(source, target, mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(TextContainer input, String text, boolean clear, MobileLookup mobileLookup) {
|
public void type(TextContainer input, String text, boolean clear, MobileLookup mobileLookup) {
|
||||||
connector.get().type(input, text, clear, mobileLookup);
|
connector.get().type(input, text, clear, mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(TextContainer input, String text, MobileLookup mobileLookup) {
|
public void type(TextContainer input, String text, MobileLookup mobileLookup) {
|
||||||
type(input, text, true, mobileLookup);
|
type(input, text, true, mobileLookup);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void type(TextContainer input, String text) {
|
public void type(TextContainer input, String text) {
|
||||||
type(input, text, true, MobileLookup.DEFAULT);
|
type(input, text, true, MobileLookup.DEFAULT);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void takeSnapshot(String prefix) {
|
public void takeSnapshot(String prefix) {
|
||||||
connector.get().takeSnapshot(getFileName(prefix, ".png"));
|
connector.get().takeSnapshot(getFileName(prefix, ".png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveSources(String viewName) {
|
public void saveSources(String viewName) {
|
||||||
connector.get().saveSource(getFileName(viewName, ".xml"));
|
connector.get().saveSource(getFileName(viewName, ".xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void captureVideo(String prefix) {
|
public void captureVideo(String prefix) {
|
||||||
connector.get().captureVideo(getFileName(prefix, ".mp4"));
|
connector.get().captureVideo(getFileName(prefix, ".mp4"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetApp() {
|
public void resetApp() {
|
||||||
connector.get().resetApp();
|
connector.get().resetApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementContent(String id, String content, MobileLookup mobileLookup) {
|
public void checkElementContent(String id, String content, MobileLookup mobileLookup) {
|
||||||
connector.get().elementsCheck().checkElementContent(id, content, mobileLookup);
|
connector.get().elementsCheck().checkElementContent(id, content, mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementContent(String id, String content) {
|
public void checkElementContent(String id, String content) {
|
||||||
connector.get().elementsCheck().checkElementContent(id, content, MobileLookup.DEFAULT);
|
connector.get().elementsCheck().checkElementContent(id, content, MobileLookup.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkElementPresent(String path, MobileLookup mobileLookup){
|
public void checkElementPresent(String path, MobileLookup mobileLookup){
|
||||||
connector.get().elementsCheck().checkElementPresent(path, mobileLookup);
|
connector.get().elementsCheck().checkElementPresent(path, mobileLookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
connector.get().close();
|
connector.get().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFileName(String prefix, String suffix) {
|
private String getFileName(String prefix, String suffix) {
|
||||||
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy/(HH.mm.ss)");
|
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy/(HH.mm.ss)");
|
||||||
Date date = new Date(System.currentTimeMillis());
|
Date date = new Date(System.currentTimeMillis());
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (prefix != null) {
|
if (prefix != null) {
|
||||||
sb.append(prefix);
|
sb.append(prefix);
|
||||||
sb.append("-");
|
sb.append("-");
|
||||||
}
|
}
|
||||||
sb.append(formatter.format(date));
|
sb.append(formatter.format(date));
|
||||||
sb.append("-");
|
sb.append("-");
|
||||||
sb.append(MobilePlatform.getMobilePlatformFromConfig(connector.get().getStore()));
|
sb.append(MobilePlatform.getMobilePlatformFromConfig(connector.get().getStore()));
|
||||||
sb.append("-");
|
sb.append("-");
|
||||||
sb.append(connector.get().getDeviceName());
|
sb.append(connector.get().getDeviceName());
|
||||||
if (suffix != null) {
|
if (suffix != null) {
|
||||||
sb.append(suffix);
|
sb.append(suffix);
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class MonetaPortalEndpoint extends WebEndpoint {
|
public class MonetaPortalEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.moneta-portal.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.moneta-portal.url";
|
||||||
|
|
||||||
public MonetaPortalEndpoint(StoreAccessor storeAccessor) {
|
public MonetaPortalEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.ExtendedRestResponse;
|
import cz.moneta.test.harness.connectors.rest.ExtendedRestResponse;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface RestEndpoint extends Endpoint {
|
public interface RestEndpoint extends Endpoint {
|
||||||
<T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers);
|
||||||
|
|
||||||
//TODO - make jackson pick up the correct generic type rather than a linked hash map
|
//TODO - make jackson pick up the correct generic type rather than a linked hash map
|
||||||
<T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse);
|
<T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse);
|
||||||
|
|
||||||
default <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(String path, Entity<?> request,
|
default <T> Pair<Integer, ExtendedRestResponse<T>> postExtended(String path, Entity<?> request,
|
||||||
Class<T> responseType,
|
Class<T> responseType,
|
||||||
Map<String, Object> headers) {
|
Map<String, Object> headers) {
|
||||||
throw new UnsupportedOperationException(String.format("Endpoint %s does not support extended request yet. Talk to the Harness support team about it", this.getClass().getSimpleName()));
|
throw new UnsupportedOperationException(String.format("Endpoint %s does not support extended request yet. Talk to the Harness support team about it", this.getClass().getSimpleName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
<T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers);
|
<T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers);
|
||||||
|
|
||||||
<T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse);
|
<T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse);
|
||||||
|
|
||||||
StoreAccessor getStore();
|
StoreAccessor getStore();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
package cz.moneta.test.harness.endpoints;
|
package cz.moneta.test.harness.endpoints;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class SmartAutoEndpoint extends WebEndpoint {
|
public class SmartAutoEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.smart-auto.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.smart-auto.url";
|
||||||
|
|
||||||
public SmartAutoEndpoint(StoreAccessor storeAccessor) {
|
public SmartAutoEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,51 +1,51 @@
|
|||||||
package cz.moneta.test.harness.endpoints.aresapi;
|
package cz.moneta.test.harness.endpoints.aresapi;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class AresApiEndpoint implements RestEndpoint {
|
public class AresApiEndpoint implements RestEndpoint {
|
||||||
|
|
||||||
private RestConnector restConnector;
|
private RestConnector restConnector;
|
||||||
private StoreAccessor store;
|
private StoreAccessor store;
|
||||||
|
|
||||||
public AresApiEndpoint(StoreAccessor store) {
|
public AresApiEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String endpointName = "endpoints.ares-api.url";
|
String endpointName = "endpoints.ares-api.url";
|
||||||
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
||||||
.map(url -> new SimpleRestConnector(url, "AresApiRestLogger"))
|
.map(url -> new SimpleRestConnector(url, "AresApiRestLogger"))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with AresApi"));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with AresApi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,50 +1,50 @@
|
|||||||
package cz.moneta.test.harness.endpoints.autoapi;
|
package cz.moneta.test.harness.endpoints.autoapi;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class AutoApiEndpoint implements RestEndpoint {
|
public class AutoApiEndpoint implements RestEndpoint {
|
||||||
private RestConnector restConnector;
|
private RestConnector restConnector;
|
||||||
private StoreAccessor store;
|
private StoreAccessor store;
|
||||||
|
|
||||||
public AutoApiEndpoint(StoreAccessor store) {
|
public AutoApiEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String endpointName = "endpoints.auto-api.url";
|
String endpointName = "endpoints.auto-api.url";
|
||||||
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
||||||
.map(url -> new SimpleRestConnector(url, "AutoApiRestLogger"))
|
.map(url -> new SimpleRestConnector(url, "AutoApiRestLogger"))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with AutoApi"));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with AutoApi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +1,50 @@
|
|||||||
package cz.moneta.test.harness.endpoints.autoapi;
|
package cz.moneta.test.harness.endpoints.autoapi;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class PSmartAutoEndpoint implements RestEndpoint {
|
public class PSmartAutoEndpoint implements RestEndpoint {
|
||||||
private RestConnector restConnector;
|
private RestConnector restConnector;
|
||||||
private StoreAccessor store;
|
private StoreAccessor store;
|
||||||
|
|
||||||
public PSmartAutoEndpoint(StoreAccessor store) {
|
public PSmartAutoEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String endpointName = "endpoints.psmartauto.url";
|
String endpointName = "endpoints.psmartauto.url";
|
||||||
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
this.restConnector = Optional.ofNullable(store.getConfig(endpointName))
|
||||||
.map(url -> new SimpleRestConnector(url, "PSmartAutoRestLogger"))
|
.map(url -> new SimpleRestConnector(url, "PSmartAutoRestLogger"))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with PSmartAuto"));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with PSmartAuto"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,47 +1,47 @@
|
|||||||
package cz.moneta.test.harness.endpoints.autodb;
|
package cz.moneta.test.harness.endpoints.autodb;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.database.OracleConnector;
|
import cz.moneta.test.harness.connectors.database.OracleConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||||
import cz.moneta.test.harness.support.auth.Credentials;
|
import cz.moneta.test.harness.support.auth.Credentials;
|
||||||
import org.jooq.DSLContext;
|
import org.jooq.DSLContext;
|
||||||
import org.jooq.Record;
|
import org.jooq.Record;
|
||||||
import org.jooq.Result;
|
import org.jooq.Result;
|
||||||
import org.jooq.Routine;
|
import org.jooq.Routine;
|
||||||
import org.jooq.TableRecord;
|
import org.jooq.TableRecord;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class AutoDBEndpoint implements Endpoint {
|
public class AutoDBEndpoint implements Endpoint {
|
||||||
|
|
||||||
private final ThreadLocal<OracleConnector> autoDbConnector = ThreadLocal.withInitial(this::initConnector);
|
private final ThreadLocal<OracleConnector> autoDbConnector = ThreadLocal.withInitial(this::initConnector);
|
||||||
|
|
||||||
private final StoreAccessor store;
|
private final StoreAccessor store;
|
||||||
|
|
||||||
public AutoDBEndpoint(StoreAccessor store) {
|
public AutoDBEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R extends TableRecord<R>> R executeDsl(Function<DSLContext, R> query) {
|
public <R extends TableRecord<R>> R executeDsl(Function<DSLContext, R> query) {
|
||||||
return autoDbConnector.get().executeDsl(query);
|
return autoDbConnector.get().executeDsl(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R extends Routine<?>> R executeProcedure(R procedure) {
|
public <R extends Routine<?>> R executeProcedure(R procedure) {
|
||||||
return autoDbConnector.get().executeProcedure(procedure);
|
return autoDbConnector.get().executeProcedure(procedure);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<Record> executeSql(String sql) {
|
public Result<Record> executeSql(String sql) {
|
||||||
return autoDbConnector.get().executeSql(sql);
|
return autoDbConnector.get().executeSql(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OracleConnector initConnector() {
|
private OracleConnector initConnector() {
|
||||||
String endpointName = "endpoints.auto-db.url";
|
String endpointName = "endpoints.auto-db.url";
|
||||||
Credentials credentials = AuthSupport.getCredentials("auto-db", store);
|
Credentials credentials = AuthSupport.getCredentials("auto-db", store);
|
||||||
return Optional.ofNullable(store.getConfig(endpointName))
|
return Optional.ofNullable(store.getConfig(endpointName))
|
||||||
.map(url -> new OracleConnector(url, credentials.getUsername(), credentials.getPassword()))
|
.map(url -> new OracleConnector(url, credentials.getUsername(), credentials.getPassword()))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with Udebs Database"));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with Udebs Database"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package cz.moneta.test.harness.endpoints.bankid;
|
||||||
|
|
||||||
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
|
public class BankIdSoniaDemoWebEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.bankid.demoapp.url";
|
||||||
|
|
||||||
|
public BankIdSoniaDemoWebEndpoint(StoreAccessor storeAccessor) {
|
||||||
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.endpoints.broadcom;
|
package cz.moneta.test.harness.endpoints.broadcom;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class BroadcomEndpoint extends WebEndpoint {
|
public class BroadcomEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.broadcom.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.broadcom.url";
|
||||||
|
|
||||||
public BroadcomEndpoint(StoreAccessor storeAccessor) {
|
public BroadcomEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,59 +1,59 @@
|
|||||||
package cz.moneta.test.harness.endpoints.cagw;
|
package cz.moneta.test.harness.endpoints.cagw;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class CaGwEndpoint implements RestEndpoint {
|
public class CaGwEndpoint implements RestEndpoint {
|
||||||
|
|
||||||
private RestConnector restConnector;
|
private RestConnector restConnector;
|
||||||
private StoreAccessor store;
|
private StoreAccessor store;
|
||||||
|
|
||||||
public CaGwEndpoint(StoreAccessor store) {
|
public CaGwEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String url = store.getConfig("endpoints.cagw.url");
|
String url = store.getConfig("endpoints.cagw.url");
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw new IllegalStateException("You need to configure endpoints.cagw.url to work with CBL");
|
throw new IllegalStateException("You need to configure endpoints.cagw.url to work with CBL");
|
||||||
}
|
}
|
||||||
this.restConnector = new SimpleRestConnector(url, "CaGwRestLogger");
|
this.restConnector = new SimpleRestConnector(url, "CaGwRestLogger");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getClientSecret(String clientKey) {
|
public String getClientSecret(String clientKey) {
|
||||||
return AuthSupport.getClientSecret(clientKey, store, HarnessConfigConstants.VAULT_CAGW_KEYS_PATH);
|
return AuthSupport.getClientSecret(clientKey, store, HarnessConfigConstants.VAULT_CAGW_KEYS_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.endpoints.cashman;
|
package cz.moneta.test.harness.endpoints.cashman;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class CashmanEndpoint extends WebEndpoint {
|
public class CashmanEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.cashman.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.cashman.url";
|
||||||
|
|
||||||
public CashmanEndpoint(StoreAccessor storeAccessor) {
|
public CashmanEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
package cz.moneta.test.harness.endpoints.cds;
|
||||||
|
|
||||||
|
import cz.moneta.test.harness.connectors.database.OracleConnector;
|
||||||
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
|
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||||
|
import cz.moneta.test.harness.support.auth.Credentials;
|
||||||
|
import org.jooq.Record;
|
||||||
|
import org.jooq.Result;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CdsEndpoint implements Endpoint {
|
||||||
|
|
||||||
|
private final StoreAccessor store;
|
||||||
|
private final ThreadLocal<OracleConnector> connector = ThreadLocal.withInitial(this::initConnector);
|
||||||
|
|
||||||
|
public CdsEndpoint(StoreAccessor store) {
|
||||||
|
this.store = store;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<Record> executeSql(String sql) {
|
||||||
|
return connector.get().executeSql(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canAccess() {
|
||||||
|
return AuthSupport.hasCredentials("cds", store);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OracleConnector initConnector() {
|
||||||
|
String endpointName = "endpoints.cds.url";
|
||||||
|
Credentials credentials = AuthSupport.getCredentials("cds", store);
|
||||||
|
return Optional.ofNullable(store.getConfig(endpointName))
|
||||||
|
.map(url -> new OracleConnector(url, credentials.getUsername(), credentials.getPassword()))
|
||||||
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointName + " to work with CDS"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +1,20 @@
|
|||||||
package cz.moneta.test.harness.endpoints.cebia;
|
package cz.moneta.test.harness.endpoints.cebia;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.BaseWsEndpoint;
|
import cz.moneta.test.harness.endpoints.BaseWsEndpoint;
|
||||||
|
|
||||||
import javax.xml.namespace.QName;
|
import javax.xml.namespace.QName;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
@Deprecated(since = "unmaintained since update to Java 17")
|
@Deprecated(since = "unmaintained since update to Java 17")
|
||||||
public class CebiaWsEndpoint extends BaseWsEndpoint {
|
public class CebiaWsEndpoint extends BaseWsEndpoint {
|
||||||
|
|
||||||
private static final String CEBIA_ADDRESS = "https://app.cebia.com/IVATEST/Services/IvaServiceActual.svc";
|
private static final String CEBIA_ADDRESS = "https://app.cebia.com/IVATEST/Services/IvaServiceActual.svc";
|
||||||
private static final URL CEBIA_WSDL_URL = CebiaWsEndpoint.class.getClassLoader().getResource("ws/IvaServiceActual.wsdl");
|
private static final URL CEBIA_WSDL_URL = CebiaWsEndpoint.class.getClassLoader().getResource("ws/IvaServiceActual.wsdl");
|
||||||
private static final QName CEBIA_SERVICE_NAME = new QName("http://schemas.cebia.com/iva/service/", "IvaService");
|
private static final QName CEBIA_SERVICE_NAME = new QName("http://schemas.cebia.com/iva/service/", "IvaService");
|
||||||
|
|
||||||
public CebiaWsEndpoint(StoreAccessor store) {
|
public CebiaWsEndpoint(StoreAccessor store) {
|
||||||
super(CEBIA_ADDRESS, CEBIA_WSDL_URL, CEBIA_SERVICE_NAME);
|
super(CEBIA_ADDRESS, CEBIA_WSDL_URL, CEBIA_SERVICE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
package cz.moneta.test.harness.endpoints.demo;
|
package cz.moneta.test.harness.endpoints.demo;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class DemoChromeWebEndpoint extends WebEndpoint {
|
public class DemoChromeWebEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.demo.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.demo.url";
|
||||||
private static final Browser DEFAULT_BROWSER = Browser.GOOGLE_CHROME;
|
private static final Browser DEFAULT_BROWSER = Browser.GOOGLE_CHROME;
|
||||||
|
|
||||||
public DemoChromeWebEndpoint(StoreAccessor storeAccessor) {
|
public DemoChromeWebEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor, DEFAULT_BROWSER);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor, DEFAULT_BROWSER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
package cz.moneta.test.harness.endpoints.demo;
|
package cz.moneta.test.harness.endpoints.demo;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class DemoEdgeWebEndpoint extends WebEndpoint {
|
public class DemoEdgeWebEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.demo.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.demo.url";
|
||||||
private static final Browser DEFAULT_BROWSER = Browser.MS_EDGE;
|
private static final Browser DEFAULT_BROWSER = Browser.MS_EDGE;
|
||||||
|
|
||||||
public DemoEdgeWebEndpoint(StoreAccessor storeAccessor) {
|
public DemoEdgeWebEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor, DEFAULT_BROWSER);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor, DEFAULT_BROWSER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,28 +1,28 @@
|
|||||||
package cz.moneta.test.harness.endpoints.demo;
|
package cz.moneta.test.harness.endpoints.demo;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.DemoConnector;
|
import cz.moneta.test.harness.connectors.DemoConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||||
|
|
||||||
public class DemoEndpoint implements Endpoint {
|
public class DemoEndpoint implements Endpoint {
|
||||||
|
|
||||||
private DemoConnector connector;
|
private DemoConnector connector;
|
||||||
|
|
||||||
public DemoEndpoint(StoreAccessor store) {
|
public DemoEndpoint(StoreAccessor store) {
|
||||||
connector = new DemoConnector();
|
connector = new DemoConnector();
|
||||||
String location = store.getConfig("endpoints.demo.spirits.location");
|
String location = store.getConfig("endpoints.demo.spirits.location");
|
||||||
connector.connectToTempDirectoryService(location);
|
connector.connectToTempDirectoryService(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void summonSpirit(String name) {
|
public void summonSpirit(String name) {
|
||||||
connector.createFile(name);
|
connector.createFile(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean spiritPresent(String name) {
|
public boolean spiritPresent(String name) {
|
||||||
return connector.fileExists(name);
|
return connector.fileExists(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expelSpirit(String name) {
|
public void expelSpirit(String name) {
|
||||||
connector.deleteFile(name);
|
connector.deleteFile(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.endpoints.dmbsib;
|
package cz.moneta.test.harness.endpoints.dmbsib;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class DmbsIbEndpoint extends WebEndpoint {
|
public class DmbsIbEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.dmbsib.web.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.dmbsib.web.url";
|
||||||
|
|
||||||
public DmbsIbEndpoint(StoreAccessor storeAccessor) {
|
public DmbsIbEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +1,50 @@
|
|||||||
package cz.moneta.test.harness.endpoints.elastic;
|
package cz.moneta.test.harness.endpoints.elastic;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ElasticReadEndpoint implements RestEndpoint {
|
public class ElasticReadEndpoint implements RestEndpoint {
|
||||||
private final RestConnector restConnector;
|
private final RestConnector restConnector;
|
||||||
private final StoreAccessor store;
|
private final StoreAccessor store;
|
||||||
|
|
||||||
public ElasticReadEndpoint(StoreAccessor store) {
|
public ElasticReadEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String endpointUrl = "endpoints.elastic.read.url";
|
String endpointUrl = "endpoints.elastic.read.url";
|
||||||
this.restConnector = Optional.ofNullable(store.getConfig(endpointUrl))
|
this.restConnector = Optional.ofNullable(store.getConfig(endpointUrl))
|
||||||
.map(url -> new SimpleRestConnector(url, "ElasticRestLogger"))
|
.map(url -> new SimpleRestConnector(url, "ElasticRestLogger"))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointUrl + " to work with Elastic Search."));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointUrl + " to work with Elastic Search."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +1,50 @@
|
|||||||
package cz.moneta.test.harness.endpoints.elastic;
|
package cz.moneta.test.harness.endpoints.elastic;
|
||||||
|
|
||||||
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
import cz.moneta.test.harness.connectors.rest.RestConnector;
|
||||||
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
import cz.moneta.test.harness.connectors.rest.SimpleRestConnector;
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
import cz.moneta.test.harness.endpoints.RestEndpoint;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import jakarta.ws.rs.client.Entity;
|
import jakarta.ws.rs.client.Entity;
|
||||||
import jakarta.ws.rs.core.GenericType;
|
import jakarta.ws.rs.core.GenericType;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ElasticWriteEndpoint implements RestEndpoint {
|
public class ElasticWriteEndpoint implements RestEndpoint {
|
||||||
private final RestConnector restConnector;
|
private final RestConnector restConnector;
|
||||||
private final StoreAccessor store;
|
private final StoreAccessor store;
|
||||||
|
|
||||||
public ElasticWriteEndpoint(StoreAccessor store) {
|
public ElasticWriteEndpoint(StoreAccessor store) {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
String endpointUrl = "endpoints.elastic.write.url";
|
String endpointUrl = "endpoints.elastic.write.url";
|
||||||
this.restConnector = Optional.ofNullable(store.getConfig(endpointUrl))
|
this.restConnector = Optional.ofNullable(store.getConfig(endpointUrl))
|
||||||
.map(url -> new SimpleRestConnector(url, "ElasticRestLogger"))
|
.map(url -> new SimpleRestConnector(url, "ElasticRestLogger"))
|
||||||
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointUrl + " to work with Elastic Search."));
|
.orElseThrow(() -> new IllegalStateException("You need to configure " + endpointUrl + " to work with Elastic Search."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> get(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.get(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> post(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
return restConnector.post(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
public <T> Pair<Integer, T> delete(String path, Map<String, Object> properties, Class<T> responseType, Map<String, Object> headers) {
|
||||||
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
return restConnector.delete(path, properties, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
public <T> Pair<Integer, T> patch(String path, Entity<?> request, Class<T> responseType, Map<String, Object> headers, boolean rawResponse) {
|
||||||
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
return restConnector.patch(path, request, new GenericType<>(responseType), headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoreAccessor getStore() {
|
public StoreAccessor getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
package cz.moneta.test.harness.endpoints.exevido;
|
package cz.moneta.test.harness.endpoints.exevido;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.data.Browser;
|
import cz.moneta.test.harness.data.Browser;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class ExevidoEndpoint extends WebEndpoint {
|
public class ExevidoEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.exevido.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.exevido.url";
|
||||||
|
|
||||||
private static final Browser DEFAULT_BROWSER = Browser.GOOGLE_CHROME;
|
private static final Browser DEFAULT_BROWSER = Browser.GOOGLE_CHROME;
|
||||||
|
|
||||||
public ExevidoEndpoint(StoreAccessor storeAccessor) {
|
public ExevidoEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::id, storeAccessor, DEFAULT_BROWSER);
|
super(ENDPOINT_URL_IDENTIFIER, By::id, storeAccessor, DEFAULT_BROWSER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.endpoints.finanso;
|
package cz.moneta.test.harness.endpoints.finanso;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class FinansoEndpoint extends WebEndpoint {
|
public class FinansoEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.finanso.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.finanso.url";
|
||||||
|
|
||||||
public FinansoEndpoint(StoreAccessor storeAccessor) {
|
public FinansoEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,14 +1,14 @@
|
|||||||
package cz.moneta.test.harness.endpoints.forte;
|
package cz.moneta.test.harness.endpoints.forte;
|
||||||
|
|
||||||
import cz.moneta.test.harness.context.StoreAccessor;
|
import cz.moneta.test.harness.context.StoreAccessor;
|
||||||
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
import cz.moneta.test.harness.endpoints.WebEndpoint;
|
||||||
import org.openqa.selenium.By;
|
import org.openqa.selenium.By;
|
||||||
|
|
||||||
public class ForteEndpoint extends WebEndpoint {
|
public class ForteEndpoint extends WebEndpoint {
|
||||||
|
|
||||||
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.forte.url";
|
private static final String ENDPOINT_URL_IDENTIFIER = "endpoints.forte.url";
|
||||||
|
|
||||||
public ForteEndpoint(StoreAccessor storeAccessor) {
|
public ForteEndpoint(StoreAccessor storeAccessor) {
|
||||||
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
super(ENDPOINT_URL_IDENTIFIER, By::xpath, storeAccessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user