jms -> mq
This commit is contained in:
parent
70c4540c4a
commit
ab7d898b8e
@ -30,7 +30,6 @@
|
||||
<commons-configuration.version>1.6</commons-configuration.version>
|
||||
<cxf.version>4.0.3</cxf.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>
|
||||
<confluent.version>7.6.0</confluent.version>
|
||||
<assertj.version>3.24.2</assertj.version>
|
||||
@ -296,12 +295,6 @@
|
||||
<artifactId>com.ibm.mq.allclient</artifactId>
|
||||
<version>${ibm.mq.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>javax.jms-api</artifactId>
|
||||
<version>${javax.jms.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Kafka dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
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.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyStore;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
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.SSLContext;
|
||||
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.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 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.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
|
||||
* multi-instance Queue Manager, SSL/TLS, and multiple message formats.
|
||||
* <p>
|
||||
* 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
|
||||
* IBM MQ connector using the native com.ibm.mq classes. Supports
|
||||
* multi-instance Queue Manager connection lists, SSL/TLS, message properties,
|
||||
* and JSON/XML/UTF-8/EBCDIC payload formats.
|
||||
*/
|
||||
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 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 JMSContext jmsContext;
|
||||
private final String connectionNameList;
|
||||
private final String channel;
|
||||
private final String queueManager;
|
||||
private final String user;
|
||||
private final String password;
|
||||
private final SSLSocketFactory sslSocketFactory;
|
||||
private final String sslCipherSuite;
|
||||
|
||||
private MQQueueManager mqQueueManager;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
String keystorePath, String keystorePassword, String sslCipherSuite) {
|
||||
this.connectionNameList = connectionNameList;
|
||||
this.channel = channel;
|
||||
this.queueManager = queueManager;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.sslCipherSuite = sslCipherSuite;
|
||||
|
||||
try {
|
||||
connectionFactory = new MQConnectionFactory();
|
||||
connectionFactory.setConnectionNameList(connectionNameList);
|
||||
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
|
||||
this.sslSocketFactory = keystorePath != null && !keystorePath.isBlank() && keystorePassword != null
|
||||
&& !keystorePassword.isBlank() ? getSslSocketFactory(keystorePath, keystorePassword) : null;
|
||||
connect();
|
||||
|
||||
} catch (Exception 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() {
|
||||
try {
|
||||
this.jmsContext = connectionFactory.createContext(user, password, JMSContext.AUTO_ACKNOWLEDGE);
|
||||
this.jmsContext.start();
|
||||
this.mqQueueManager = new MQQueueManager(queueManager, createConnectionProperties());
|
||||
LOG.info("Connected to IBM MQ: {}", queueManager);
|
||||
} catch (Exception e) {
|
||||
} catch (MQException e) {
|
||||
throw new MessagingConnectionException(
|
||||
"Failed to connect to IBM MQ: " + queueManager + " - " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private Hashtable<String, Object> createConnectionProperties() {
|
||||
Hashtable<String, Object> properties = new Hashtable<>();
|
||||
if (connectionNameList != null && !connectionNameList.isBlank()) {
|
||||
properties.put(WMQConstants.WMQ_CONNECTION_NAME_LIST, connectionNameList);
|
||||
}
|
||||
properties.put(CMQC.CHANNEL_PROPERTY, channel);
|
||||
properties.put(CMQC.TRANSPORT_PROPERTY, CMQC.TRANSPORT_MQSERIES_CLIENT);
|
||||
|
||||
if (user != null && !user.isBlank()) {
|
||||
properties.put(CMQC.USER_ID_PROPERTY, user);
|
||||
}
|
||||
if (password != null && !password.isBlank()) {
|
||||
properties.put(CMQC.PASSWORD_PROPERTY, password);
|
||||
}
|
||||
if (sslSocketFactory != null) {
|
||||
properties.put(CMQC.SSL_SOCKET_FACTORY_PROPERTY, sslSocketFactory);
|
||||
}
|
||||
if (sslCipherSuite != null && !sslCipherSuite.isBlank()) {
|
||||
properties.put(CMQC.SSL_CIPHER_SUITE_PROPERTY, sslCipherSuite);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a JSON or XML message as TextMessage.
|
||||
* Send a JSON or XML message as MQ string message.
|
||||
*/
|
||||
private void sendTextMessage(String queueName, String payload, Map<String, String> properties) {
|
||||
javax.jms.Queue queue = getQueue(queueName);
|
||||
|
||||
TextMessage message = jmsContext.createTextMessage(payload);
|
||||
|
||||
// Set JMS properties
|
||||
if (properties != null) {
|
||||
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
||||
try {
|
||||
if (entry.getKey().equals(ImqRequest.PROP_JMS_CORRELATION_ID)) {
|
||||
message.setJMSCorrelationID(entry.getValue());
|
||||
continue;
|
||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_TYPE)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
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);
|
||||
sendMessage(queueName, payload.getBytes(UTF_8), UTF_8, 1208, CMQC.MQFMT_STRING, properties, "JSON/XML");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as BytesMessage with specific encoding and CCSID.
|
||||
* Send a message as raw bytes with specific encoding and CCSID.
|
||||
*/
|
||||
private void sendBytesMessage(String queueName, String payload, Charset charset, int ccsid,
|
||||
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();
|
||||
|
||||
// Convert payload to bytes using specified charset
|
||||
byte[] bytes = payload.getBytes(charset);
|
||||
private void sendMessage(String queueName, byte[] payload, Charset charset, int ccsid, String mqFormat,
|
||||
Map<String, String> properties, String logFormat) {
|
||||
MQQueue queue = null;
|
||||
try {
|
||||
message.writeBytes(bytes);
|
||||
int openOptions = CMQC.MQOO_OUTPUT | CMQC.MQOO_FAIL_IF_QUIESCING;
|
||||
|
||||
queue = openQueue(queueName, openOptions);
|
||||
|
||||
MQMessage message = new MQMessage();
|
||||
message.format = mqFormat;
|
||||
message.characterSet = ccsid;
|
||||
message.write(payload);
|
||||
if (!CMQC.MQFMT_STRING.equals(mqFormat)) {
|
||||
message.setIntProperty("CCSID", ccsid);
|
||||
} catch (JMSException e) {
|
||||
throw new MessagingDestinationException("Failed to create bytes message", e);
|
||||
}
|
||||
|
||||
// Set JMS properties
|
||||
if (properties != null) {
|
||||
for (Map.Entry<String, String> entry : properties.entrySet()) {
|
||||
try {
|
||||
if (entry.getKey().equals(ImqRequest.PROP_JMS_CORRELATION_ID)) {
|
||||
message.setJMSCorrelationID(entry.getValue());
|
||||
continue;
|
||||
} else if (entry.getKey().equals(ImqRequest.PROP_JMS_TYPE)) {
|
||||
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);
|
||||
|
||||
try {
|
||||
jmsContext.createProducer().send(queue, message);
|
||||
} catch (RuntimeException e) {
|
||||
queue.put(message, putOptions);
|
||||
} catch (MQException | IOException e) {
|
||||
throw new MessagingDestinationException("Failed to send message to queue: " + queueName, e);
|
||||
} finally {
|
||||
closeQueue(queue, queueName);
|
||||
}
|
||||
LOG.debug("Sent {} message to queue: {}", charset, queueName);
|
||||
|
||||
LOG.debug("Sent {} message ({}) to queue: {}", logFormat, charset, queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +195,7 @@ public class IbmMqConnector implements Connector {
|
||||
* @param queueName Queue name
|
||||
* @param payload Message payload
|
||||
* @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) {
|
||||
switch (format) {
|
||||
@ -234,64 +209,108 @@ public class IbmMqConnector implements Connector {
|
||||
* Receive a message from a queue with timeout.
|
||||
*
|
||||
* @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 timeout Timeout duration
|
||||
* @return Received message
|
||||
*/
|
||||
public ReceivedMessage receive(String queueName, String messageSelector, MqMessageFormat format,
|
||||
java.time.Duration timeout) {
|
||||
long timeoutMs = timeout.toMillis();
|
||||
public ReceivedMessage receive(String queueName, String messageSelector, MqMessageFormat format, Duration timeout) {
|
||||
if (messageSelector == null || messageSelector.isBlank()) {
|
||||
return receiveNext(queueName, format, timeout);
|
||||
}
|
||||
|
||||
javax.jms.Queue queue = getQueue(queueName);
|
||||
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;
|
||||
return receiveMatching(queueName, messageSelector, format, timeout);
|
||||
}
|
||||
|
||||
private ReceivedMessage receiveNext(String queueName, MqMessageFormat format, Duration timeout) {
|
||||
MQQueue queue = null;
|
||||
try {
|
||||
while (remainingTime > 0 && !messageFound.get()) {
|
||||
Message message = consumer.receive(remainingTime);
|
||||
queue = openQueue(queueName, CMQC.MQOO_INPUT_SHARED | CMQC.MQOO_FAIL_IF_QUIESCING);
|
||||
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) {
|
||||
received = decodeMessage(message, queueName, format);
|
||||
messageFound.set(true);
|
||||
} else {
|
||||
// Exponential backoff
|
||||
pollInterval = Math.min(pollInterval * 2, DEFAULT_MAX_POLL_INTERVAL_MS);
|
||||
remainingTime -= pollInterval;
|
||||
}
|
||||
}
|
||||
|
||||
if (received == null) {
|
||||
queue.get(message, getOptions);
|
||||
return decodeMessage(message, queueName, format);
|
||||
} catch (MQException e) {
|
||||
if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) {
|
||||
throw new MessagingTimeoutException("No message matching filter found on queue '" + queueName
|
||||
+ "' 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);
|
||||
} finally {
|
||||
try {
|
||||
consumer.close();
|
||||
} catch (JMSRuntimeException e) {
|
||||
LOG.warn("Failed to close consumer", e);
|
||||
closeQueue(queue, queueName);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse a queue (non-destructive read).
|
||||
*
|
||||
* @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 maxMessages Maximum number of messages to browse
|
||||
* @return List of received messages
|
||||
@ -299,170 +318,268 @@ public class IbmMqConnector implements Connector {
|
||||
public List<ReceivedMessage> browse(String queueName, String messageSelector, MqMessageFormat format,
|
||||
int maxMessages) {
|
||||
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())
|
||||
? jmsContext.createBrowser(queue)
|
||||
: jmsContext.createBrowser(queue, messageSelector)) {
|
||||
try {
|
||||
queue = openQueue(queueName, CMQC.MQOO_BROWSE | CMQC.MQOO_FAIL_IF_QUIESCING);
|
||||
|
||||
Enumeration<?> enumeration = browser.getEnumeration();
|
||||
int count = 0;
|
||||
MQGetMessageOptions browseOptions = new MQGetMessageOptions();
|
||||
browseOptions.options = CMQC.MQGMO_BROWSE_FIRST | CMQC.MQGMO_NO_SYNCPOINT | CMQC.MQGMO_FAIL_IF_QUIESCING;
|
||||
|
||||
while (enumeration.hasMoreElements() && count < maxMessages) {
|
||||
Message message = (Message) enumeration.nextElement();
|
||||
if (message != null) {
|
||||
ReceivedMessage received = decodeMessage(message, queueName, format);
|
||||
messages.add(received);
|
||||
count++;
|
||||
while (messages.size() < maxMessages) {
|
||||
MQMessage message = new MQMessage();
|
||||
try {
|
||||
queue.get(message, browseOptions);
|
||||
} catch (MQException e) {
|
||||
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;
|
||||
} catch (JMSException e) {
|
||||
} catch (MQException 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) {
|
||||
long timestamp;
|
||||
try {
|
||||
timestamp = jmsMessage.getJMSTimestamp();
|
||||
} catch (JMSException e) {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
private ReceivedMessage decodeMessage(MQMessage mqMessage, String queueName, MqMessageFormat format) {
|
||||
long timestamp = mqMessage.putDateTime != null ? mqMessage.putDateTime.getTimeInMillis()
|
||||
: System.currentTimeMillis();
|
||||
if (timestamp == 0) {
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
extractMqHeadersAndProperties(mqMessage, headers, queueName);
|
||||
|
||||
byte[] data = readMessageBody(mqMessage);
|
||||
String body;
|
||||
MessageContentType contentType;
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
|
||||
// Extract JMS properties as headers
|
||||
extractJmsProperties(jmsMessage, headers);
|
||||
|
||||
if (jmsMessage instanceof TextMessage textMessage) {
|
||||
try {
|
||||
body = textMessage.getText();
|
||||
} catch (JMSException e) {
|
||||
throw new RuntimeException("Failed to read text message body", e);
|
||||
switch (format) {
|
||||
case XML -> {
|
||||
body = new String(data, charsetFor(mqMessage.characterSet, UTF_8));
|
||||
contentType = MessageContentType.XML;
|
||||
}
|
||||
contentType = switch (format) {
|
||||
case XML -> MessageContentType.XML;
|
||||
default -> MessageContentType.JSON;
|
||||
};
|
||||
} else if (jmsMessage instanceof BytesMessage bytesMessage) {
|
||||
int ccsid;
|
||||
try {
|
||||
ccsid = bytesMessage.getIntProperty("CCSID");
|
||||
} catch (JMSException e) {
|
||||
ccsid = 1208; // default UTF-8
|
||||
case JSON -> {
|
||||
body = new String(data, charsetFor(mqMessage.characterSet, UTF_8));
|
||||
contentType = MessageContentType.JSON;
|
||||
}
|
||||
body = decodeBytesMessage(bytesMessage, ccsid);
|
||||
case EBCDIC_870 -> {
|
||||
body = new String(data, EBCDIC_870);
|
||||
contentType = MessageContentType.RAW_TEXT;
|
||||
}
|
||||
case UTF8_1208 -> {
|
||||
body = new String(data, UTF_8);
|
||||
contentType = MessageContentType.RAW_TEXT;
|
||||
}
|
||||
default -> {
|
||||
body = new String(data, UTF_8);
|
||||
contentType = MessageContentType.RAW_TEXT;
|
||||
} else {
|
||||
try {
|
||||
throw new IllegalArgumentException("Unsupported message type: " + jmsMessage.getJMSType());
|
||||
} catch (JMSException e) {
|
||||
throw new IllegalArgumentException("Unsupported message type", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new ReceivedMessage(body, contentType, headers, timestamp, queueName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode BytesMessage body based on CCSID.
|
||||
*/
|
||||
private String decodeBytesMessage(BytesMessage bytesMessage, int ccsid) {
|
||||
private byte[] readMessageBody(MQMessage message) {
|
||||
try {
|
||||
long bodyLength;
|
||||
try {
|
||||
bodyLength = bytesMessage.getBodyLength();
|
||||
} catch (JMSException e) {
|
||||
throw new RuntimeException("Failed to get message body length", e);
|
||||
}
|
||||
byte[] data = new byte[(int) bodyLength];
|
||||
bytesMessage.readBytes(data);
|
||||
|
||||
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);
|
||||
message.seek(0);
|
||||
int length = message.getMessageLength();
|
||||
byte[] data = new byte[length];
|
||||
message.readFully(data);
|
||||
return data;
|
||||
} catch (EOFException e) {
|
||||
throw new RuntimeException("Failed to seek message body", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read message body", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract JMS properties as headers.
|
||||
* Extract MQ headers and message properties as headers.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void extractJmsProperties(Message message, Map<String, String> headers) {
|
||||
private void extractMqHeadersAndProperties(MQMessage message, Map<String, String> headers, String queueName) {
|
||||
headers.put("JMSMessageID", toJmsId(message.messageId));
|
||||
headers.put("JMSCorrelationID", toJmsId(message.correlationId));
|
||||
headers.put("JMSType", getStringProperty(message, ImqRequest.PROP_JMS_TYPE, ""));
|
||||
headers.put("JMSDestination", queueName);
|
||||
headers.put("JMSDeliveryMode", String.valueOf(message.persistence));
|
||||
headers.put("JMSPriority", String.valueOf(message.priority));
|
||||
headers.put("JMSTimestamp",
|
||||
message.putDateTime != null ? String.valueOf(message.putDateTime.getTimeInMillis()) : "");
|
||||
headers.put("MQFormat", message.format != null ? message.format.trim() : "");
|
||||
headers.put("MQCharacterSet", String.valueOf(message.characterSet));
|
||||
headers.put("MQEncoding", String.valueOf(message.encoding));
|
||||
headers.put("MQBackoutCount", String.valueOf(message.backoutCount));
|
||||
headers.put("MQReplyToQueue", message.replyToQueueName != null ? message.replyToQueueName.trim() : "");
|
||||
headers.put("MQReplyToQueueManager",
|
||||
message.replyToQueueManagerName != null ? message.replyToQueueManagerName.trim() : "");
|
||||
headers.put("MQUserId", message.userId != null ? message.userId.trim() : "");
|
||||
|
||||
Enumeration<String> propertyNames;
|
||||
try {
|
||||
// Common JMS headers
|
||||
headers.put("JMSMessageID", message.getJMSMessageID());
|
||||
try {
|
||||
headers.put("JMSType", message.getJMSType() != null ? message.getJMSType() : "");
|
||||
} catch (JMSException e) {
|
||||
headers.put("JMSType", "");
|
||||
}
|
||||
try {
|
||||
headers.put("JMSDestination",
|
||||
message.getJMSDestination() != null ? message.getJMSDestination().toString() : "");
|
||||
} catch (JMSException e) {
|
||||
headers.put("JMSDestination", "");
|
||||
}
|
||||
try {
|
||||
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", "");
|
||||
propertyNames = message.getPropertyNames("%");
|
||||
} catch (MQException e) {
|
||||
LOG.warn("Failed to extract MQ properties", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract custom properties
|
||||
Enumeration<String> propertyNames = (Enumeration<String>) message.getPropertyNames();
|
||||
while (propertyNames.hasMoreElements()) {
|
||||
String propName = propertyNames.nextElement();
|
||||
try {
|
||||
Object propValue = message.getObjectProperty(propName);
|
||||
if (propValue != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Queue object from queue name.
|
||||
*/
|
||||
private javax.jms.Queue getQueue(String queueName) {
|
||||
return jmsContext.createQueue(queueName);
|
||||
private void applyMessageProperties(MQMessage message, Map<String, String> properties) {
|
||||
if (properties == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
public void close() {
|
||||
if (jmsContext != null) {
|
||||
if (mqQueueManager != null && mqQueueManager.isConnected()) {
|
||||
try {
|
||||
jmsContext.close();
|
||||
mqQueueManager.disconnect();
|
||||
LOG.info("Closed connection to IBM MQ: {}", queueManager);
|
||||
} catch (Exception e) {
|
||||
} catch (MQException e) {
|
||||
LOG.error("Failed to close IBM MQ connection", e);
|
||||
}
|
||||
}
|
||||
@ -499,4 +616,34 @@ public class IbmMqConnector implements Connector {
|
||||
|
||||
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,16 +1,17 @@
|
||||
package cz.moneta.test.harness.connectors.messaging;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import cz.moneta.test.harness.connectors.messaging.kafkautils.CustomKafkaAvroDeserializer;
|
||||
import cz.moneta.test.harness.connectors.messaging.kafkautils.JsonToAvroConverter;
|
||||
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.MessagingSchemaException;
|
||||
import cz.moneta.test.harness.support.messaging.exception.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.messaging.kafka.MessageContentType;
|
||||
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;
|
||||
import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
|
||||
import io.confluent.kafka.schemaregistry.client.SchemaRegistryClientConfig;
|
||||
import io.confluent.kafka.serializers.AbstractKafkaSchemaSerDeConfig;
|
||||
import io.confluent.kafka.serializers.KafkaAvroSerializer;
|
||||
import org.apache.avro.generic.GenericRecord;
|
||||
import org.apache.kafka.clients.consumer.ConsumerConfig;
|
||||
import org.apache.kafka.clients.consumer.ConsumerRecord;
|
||||
@ -23,18 +24,14 @@ import org.apache.kafka.common.TopicPartition;
|
||||
import org.apache.kafka.common.header.Headers;
|
||||
import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
import org.apache.kafka.common.serialization.StringSerializer;
|
||||
import org.slf4j.Logger;
|
||||
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 cz.moneta.test.harness.messaging.exception.MessagingSchemaException;
|
||||
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.messaging.kafka.MessageContentType;
|
||||
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;
|
||||
import io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient;
|
||||
import io.confluent.kafka.serializers.KafkaAvroDeserializer;
|
||||
import io.confluent.kafka.serializers.KafkaAvroSerializer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Kafka connector for sending and receiving messages.
|
||||
@ -45,7 +42,7 @@ import io.confluent.kafka.serializers.KafkaAvroSerializer;
|
||||
*/
|
||||
public class KafkaConnector implements cz.moneta.test.harness.connectors.Connector {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(KafkaConnector.class);
|
||||
private static final Logger LOG = LogManager.getLogger(KafkaConnector.class);
|
||||
|
||||
private final Properties producerConfig;
|
||||
private final Properties consumerConfig;
|
||||
@ -70,35 +67,45 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
String schemaRegistryApiKey,
|
||||
String schemaRegistryApiSecret) {
|
||||
this.schemaRegistryUrl = schemaRegistryUrl;
|
||||
this.schemaRegistryClient = new CachedSchemaRegistryClient(
|
||||
Collections.singletonList(schemaRegistryUrl), 100, new HashMap<>());
|
||||
|
||||
this.producerConfig = createProducerConfig(bootstrapServers, apiKey, apiSecret);
|
||||
this.consumerConfig = createConsumerConfig(bootstrapServers, schemaRegistryApiKey, schemaRegistryApiSecret);
|
||||
HashMap<String, String> schemaRegistryProps = new HashMap<>();
|
||||
schemaRegistryProps.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||
schemaRegistryProps.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||
this.schemaRegistryClient = new CachedSchemaRegistryClient(
|
||||
Collections.singletonList(schemaRegistryUrl), 100, schemaRegistryProps);
|
||||
|
||||
this.producerConfig = createProducerConfig(bootstrapServers, apiKey, apiSecret, schemaRegistryApiKey, schemaRegistryApiSecret);
|
||||
this.consumerConfig = createConsumerConfig(bootstrapServers, apiKey, apiSecret, schemaRegistryApiKey, schemaRegistryApiSecret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates producer configuration.
|
||||
*/
|
||||
private Properties createProducerConfig(String bootstrapServers, String apiKey, String apiSecret) {
|
||||
private Properties createProducerConfig(String bootstrapServers, String apiKey, String apiSecret,
|
||||
String schemaRegistryApiKey, String schemaRegistryApiSecret) {
|
||||
Properties config = new Properties();
|
||||
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
|
||||
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
|
||||
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class);
|
||||
config.put("schema.registry.url", schemaRegistryUrl);
|
||||
config.put(ProducerConfig.ACKS_CONFIG, "all");
|
||||
config.put(ProducerConfig.LINGER_MS_CONFIG, 1);
|
||||
config.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
|
||||
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.AUTO_REGISTER_SCHEMAS, false);
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.USE_LATEST_VERSION, true);
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
|
||||
config.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||
config.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||
|
||||
// SASL/PLAIN authentication
|
||||
// config.put("security.protocol", "SASL_SSL");
|
||||
// config.put("sasl.mechanism", "PLAIN");
|
||||
// config.put("sasl.jaas.config",
|
||||
// "org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
||||
// "username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
||||
config.put("security.protocol", "SASL_SSL");
|
||||
config.put("sasl.mechanism", "PLAIN");
|
||||
config.put("sasl.jaas.config",
|
||||
"org.apache.kafka.common.security.plain.PlainLoginModule required " +
|
||||
"username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
||||
|
||||
// SSL configuration
|
||||
// config.put("ssl.endpoint.identification.algorithm", "https");
|
||||
config.put("ssl.endpoint.identification.algorithm", "https");
|
||||
|
||||
return config;
|
||||
}
|
||||
@ -106,14 +113,16 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
/**
|
||||
* Creates consumer configuration.
|
||||
*/
|
||||
private Properties createConsumerConfig(String bootstrapServers, String apiKey, String apiSecret) {
|
||||
private Properties createConsumerConfig(String bootstrapServers, String apiKey, String apiSecret,
|
||||
String schemaRegistryApiKey, String schemaRegistryApiSecret) {
|
||||
Properties config = new Properties();
|
||||
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.VALUE_DESERIALIZER_CLASS_CONFIG, CustomKafkaAvroDeserializer.class);
|
||||
config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "none");
|
||||
config.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
|
||||
|
||||
|
||||
// SASL/PLAIN authentication
|
||||
config.put("security.protocol", "SASL_SSL");
|
||||
config.put("sasl.mechanism", "PLAIN");
|
||||
@ -122,7 +131,12 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
"username=\"" + apiKey + "\" password=\"" + apiSecret + "\";");
|
||||
|
||||
// Schema Registry for deserialization
|
||||
config.put("schema.registry.url", schemaRegistryUrl);
|
||||
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.AUTO_REGISTER_SCHEMAS, false);
|
||||
config.put(AbstractKafkaSchemaSerDeConfig.USE_LATEST_VERSION, true);
|
||||
config.put(SchemaRegistryClientConfig.BASIC_AUTH_CREDENTIALS_SOURCE, "USER_INFO");
|
||||
config.put(SchemaRegistryClientConfig.USER_INFO_CONFIG, schemaRegistryApiKey + ":" + schemaRegistryApiSecret);
|
||||
config.put("specific.avro.reader", false);
|
||||
|
||||
// SSL configuration
|
||||
@ -179,7 +193,7 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
*/
|
||||
public List<ReceivedMessage> receive(String topic,
|
||||
Predicate<ReceivedMessage> filter,
|
||||
Duration timeout) {
|
||||
Duration timeout, boolean startFromBeginning) {
|
||||
KafkaConsumer<String, GenericRecord> consumer = null;
|
||||
try {
|
||||
consumer = new KafkaConsumer<>(consumerConfig);
|
||||
@ -191,10 +205,12 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
"Topic '" + topic + "' does not exist or has no partitions");
|
||||
}
|
||||
|
||||
// Assign partitions and seek to end
|
||||
consumer.assign(partitions);
|
||||
// consumer.seekToBeginning(partitions);
|
||||
if (startFromBeginning) {
|
||||
consumer.seekToBeginning(partitions);
|
||||
} else {
|
||||
consumer.seekToEnd(partitions);
|
||||
}
|
||||
|
||||
// Poll loop with exponential backoff
|
||||
long startTime = System.currentTimeMillis();
|
||||
@ -310,8 +326,12 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
*/
|
||||
private ReceivedMessage convertToReceivedMessage(ConsumerRecord<String, GenericRecord> record, String topic) {
|
||||
try {
|
||||
String jsonBody = avroToJson(record.value());
|
||||
|
||||
String jsonBody;
|
||||
if (null != record.value()) {
|
||||
jsonBody = avroToJson(record.value());
|
||||
} else {
|
||||
jsonBody = "";
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Headers kafkaHeaders = record.headers();
|
||||
for (org.apache.kafka.common.header.Header header : kafkaHeaders) {
|
||||
@ -340,6 +360,7 @@ public class KafkaConnector implements cz.moneta.test.harness.connectors.Connect
|
||||
*/
|
||||
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,22 +1,16 @@
|
||||
package cz.moneta.test.harness.connectors.messaging;
|
||||
package cz.moneta.test.harness.connectors.messaging.kafkautils;
|
||||
|
||||
import com.google.gson.*;
|
||||
import org.apache.avro.Schema;
|
||||
import org.apache.avro.generic.GenericData;
|
||||
import org.apache.avro.generic.GenericDatumReader;
|
||||
import org.apache.avro.generic.GenericDatumWriter;
|
||||
import org.apache.avro.generic.GenericRecord;
|
||||
import org.apache.avro.io.DatumReader;
|
||||
import org.apache.avro.io.DatumWriter;
|
||||
import org.apache.avro.io.Decoder;
|
||||
import org.apache.avro.io.JsonDecoder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonToAvroConverter {
|
||||
|
||||
protected static GenericRecord processJson(String json, Schema schema) throws IllegalArgumentException, JsonSchemaException {
|
||||
public static GenericRecord processJson(String json, Schema schema) throws IllegalArgumentException, JsonSchemaException {
|
||||
GenericRecord result = (GenericRecord) jsonElementToAvro(JsonParser.parseString(json), schema);
|
||||
return result;
|
||||
}
|
||||
@ -7,6 +7,7 @@ public final class HarnessConfigConstants {
|
||||
public static final String VAULT_USERNAME_CONFIG = "vault.username";
|
||||
public static final String VAULT_PASSWORD_CONFIG = "vault.password";
|
||||
public static final String VAULT_URL_CONFIG = "vault.url";
|
||||
public static final String VAULT_KAFKA_KEYS_CONFIG = "vault.kafka.secrets.path";
|
||||
public static final String VAULT_WSO2_KEYS_PATH = "vault.client.secrets.path";
|
||||
public static final String VAULT_CAGW_KEYS_PATH = "vault.cagw.client.secrets.path";
|
||||
public static final String ENVIRONMENT_TYPE = "environment.type";
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -7,13 +7,12 @@ import java.util.Optional;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cz.moneta.test.harness.connectors.VaultConnector;
|
||||
import cz.moneta.test.harness.connectors.messaging.IbmMqConnector;
|
||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||
import cz.moneta.test.harness.messaging.MqMessageFormat;
|
||||
import cz.moneta.test.harness.messaging.ReceivedMessage;
|
||||
import cz.moneta.test.harness.support.messaging.MqMessageFormat;
|
||||
import cz.moneta.test.harness.support.messaging.ReceivedMessage;
|
||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||
import cz.moneta.test.harness.support.auth.Credentials;
|
||||
|
||||
/**
|
||||
@ -38,8 +37,7 @@ public class ImqFirstVisionEndpoint implements Endpoint {
|
||||
private static final String SSL_CIPHER_SUITE_KEY = "endpoints.imq-first-vision.ssl-cipher-suite";
|
||||
private static final String VAULT_PATH_KEY = "vault.imq-first-vision.secrets.path";
|
||||
private static final String VAULT_KEYSTORE_PASSWORD_KEY = "keystorePassword";
|
||||
|
||||
private static final String KEYSTORE_PATH = "keystores/imq-keystore.jks";
|
||||
private static final String KEYSTORE_PATH = "keystores/imq.jks";
|
||||
|
||||
/**
|
||||
* Constructor that reads configuration from StoreAccessor.
|
||||
@ -79,25 +77,10 @@ public class ImqFirstVisionEndpoint implements Endpoint {
|
||||
*/
|
||||
private void loadCredentialsFromVault() {
|
||||
try {
|
||||
// Get vault URL from configuration
|
||||
String vaultPath = getConfig(VAULT_PATH_KEY);
|
||||
String vaultUrl = getConfig(HarnessConfigConstants.VAULT_URL_CONFIG);
|
||||
String vaultUser = getConfig(HarnessConfigConstants.VAULT_USERNAME_CONFIG);
|
||||
String vaultPassword = getConfig(HarnessConfigConstants.VAULT_PASSWORD_CONFIG);
|
||||
|
||||
VaultConnector vaultConnector = new VaultConnector(vaultUrl, vaultUser, vaultPassword);
|
||||
|
||||
Optional<Credentials> credentials = vaultConnector.getUsernameAndPassword(vaultPath);
|
||||
|
||||
if (credentials.isPresent()) {
|
||||
this.username = credentials.get().getUsername();
|
||||
this.password = credentials.get().getPassword();
|
||||
this.keystorePassword = vaultConnector.getValue(vaultPath, VAULT_KEYSTORE_PASSWORD_KEY)
|
||||
.map(Object::toString).orElse(null);
|
||||
LOG.info("Successfully loaded credentials from Vault for path: {}", vaultPath);
|
||||
} else {
|
||||
throw new IllegalStateException("Credentials not found in Vault at path: " + vaultPath);
|
||||
}
|
||||
Credentials credentials = AuthSupport.getCredentials("imq-first-vision", store);
|
||||
this.username = credentials.getUsername();
|
||||
this.password = credentials.getPassword();
|
||||
this.keystorePassword = AuthSupport.getClientSecret(VAULT_KEYSTORE_PASSWORD_KEY, store, VAULT_PATH_KEY);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to load credentials from Vault", e);
|
||||
}
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
package cz.moneta.test.harness.endpoints.kafka;
|
||||
|
||||
import cz.moneta.test.harness.connectors.messaging.KafkaConnector;
|
||||
import cz.moneta.test.harness.context.BaseStoreAccessor;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.Endpoint;
|
||||
import cz.moneta.test.harness.messaging.exception.MessagingConnectionException;
|
||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||
import cz.moneta.test.harness.support.messaging.kafka.ReceivedMessage;
|
||||
import com.bettercloud.vault.Vault;
|
||||
import com.bettercloud.vault.VaultConfig;
|
||||
import com.bettercloud.vault.VaultException;
|
||||
import com.bettercloud.vault.response.LogicalResponse;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cz.moneta.test.harness.constants.HarnessConfigConstants.VAULT_KAFKA_KEYS_CONFIG;
|
||||
|
||||
/**
|
||||
* Kafka endpoint that provides high-level API for Kafka messaging.
|
||||
@ -55,16 +50,10 @@ public class KafkaEndpoint implements Endpoint {
|
||||
}
|
||||
|
||||
// Retrieve credentials from Vault
|
||||
String vaultPath = store.getConfig("vault.kafka.secrets.path");
|
||||
if (vaultPath == null) {
|
||||
throw new IllegalStateException(
|
||||
"You need to configure vault.kafka.secrets.path");
|
||||
}
|
||||
//
|
||||
String apiKey = getVaultValue(vaultPath, "apiKey");
|
||||
String apiSecret = getVaultValue(vaultPath, "apiSecret");
|
||||
String schemaRegistryApiKey = getVaultValue(vaultPath, "schemaRegistryApiKey");
|
||||
String schemaRegistryApiSecret = getVaultValue(vaultPath, "schemaRegistryApiSecret");
|
||||
String apiKey = getVaultValue("apiKey");
|
||||
String apiSecret = getVaultValue("apiSecret");
|
||||
String schemaRegistryApiKey = getVaultValue("schemaRegistryApiKey");
|
||||
String schemaRegistryApiSecret = getVaultValue("schemaRegistryApiSecret");
|
||||
|
||||
// Create connector
|
||||
this.connector = new KafkaConnector(
|
||||
@ -99,16 +88,16 @@ public class KafkaEndpoint implements Endpoint {
|
||||
*/
|
||||
public List<ReceivedMessage> receive(String topic,
|
||||
java.util.function.Predicate<ReceivedMessage> filter,
|
||||
Duration timeout) {
|
||||
return connector.receive(topic, filter, timeout);
|
||||
Duration timeout, boolean startFromBeginning) {
|
||||
return connector.receive(topic, filter, timeout, startFromBeginning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a message with default timeout (30 seconds).
|
||||
*/
|
||||
public List<ReceivedMessage> receive(String topic,
|
||||
java.util.function.Predicate<ReceivedMessage> filter) {
|
||||
return receive(topic, filter, Duration.ofSeconds(30));
|
||||
java.util.function.Predicate<ReceivedMessage> filter, boolean startFromBeginning) {
|
||||
return receive(topic, filter, Duration.ofSeconds(30), startFromBeginning);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,8 +107,8 @@ public class KafkaEndpoint implements Endpoint {
|
||||
* @param timeout Maximum time to wait
|
||||
* @return First message
|
||||
*/
|
||||
public List<ReceivedMessage> receive(String topic, Duration timeout) {
|
||||
return receive(topic, msg -> true, timeout);
|
||||
public List<ReceivedMessage> receive(String topic, Duration timeout, boolean startFromBeginning) {
|
||||
return receive(topic, msg -> true, timeout, startFromBeginning);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,36 +153,7 @@ public class KafkaEndpoint implements Endpoint {
|
||||
/**
|
||||
* Retrieves a value from Vault.
|
||||
*/
|
||||
private String getVaultValue(String path, String key) {
|
||||
try {
|
||||
VaultConfig vaultConfig = new VaultConfig()
|
||||
.address(store.getConfig("vault.address", "http://localhost:8200"))
|
||||
.token(store.getConfig("vault.token"))
|
||||
.build();
|
||||
Vault vault = new Vault(vaultConfig, 2);
|
||||
|
||||
LogicalResponse response = vault.logical().read(path);
|
||||
if (response == null) {
|
||||
throw new MessagingConnectionException(
|
||||
"Failed to read from Vault path: " + path);
|
||||
}
|
||||
Map<String, String> data = response.getData();
|
||||
if (data == null) {
|
||||
throw new MessagingConnectionException(
|
||||
"No data found in Vault path: " + path);
|
||||
}
|
||||
|
||||
String value = data.get(key);
|
||||
if (value == null) {
|
||||
throw new MessagingConnectionException(
|
||||
"Credential '" + key + "' not found in Vault path: " + path);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
} catch (VaultException e) {
|
||||
throw new MessagingConnectionException(
|
||||
"Failed to retrieve credential '" + key + "' from Vault at " + path, e);
|
||||
}
|
||||
private String getVaultValue(String key) {
|
||||
return AuthSupport.getClientSecret(key, store, VAULT_KAFKA_KEYS_CONFIG);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
package cz.moneta.test.harness.messaging;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
|
||||
/**
|
||||
* Wrapper for extracted JSON path values.
|
||||
* Provides fluent methods for value extraction and conversion.
|
||||
*/
|
||||
public class JsonPathValue {
|
||||
|
||||
private final JsonNode node;
|
||||
private final String rawValue;
|
||||
|
||||
public JsonPathValue(JsonNode node) {
|
||||
this.node = node;
|
||||
this.rawValue = node != null ? node.asText() : null;
|
||||
}
|
||||
|
||||
public JsonPathValue(String rawValue) {
|
||||
this.node = null;
|
||||
this.rawValue = rawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a string.
|
||||
*/
|
||||
public String asText() {
|
||||
if (node != null && !(node instanceof NullNode)) {
|
||||
return node.asText();
|
||||
}
|
||||
return rawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as an integer.
|
||||
*/
|
||||
public int asInt() {
|
||||
if (node != null && !(node instanceof NullNode)) {
|
||||
return node.asInt();
|
||||
}
|
||||
return Integer.parseInt(rawValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a long.
|
||||
*/
|
||||
public long asLong() {
|
||||
if (node != null && !(node instanceof NullNode)) {
|
||||
return node.asLong();
|
||||
}
|
||||
return Long.parseLong(rawValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value as a boolean.
|
||||
*/
|
||||
public boolean asBoolean() {
|
||||
if (node != null && !(node instanceof NullNode)) {
|
||||
return node.asBoolean();
|
||||
}
|
||||
return Boolean.parseBoolean(rawValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value is null or missing.
|
||||
*/
|
||||
public boolean isNull() {
|
||||
return node == null || node instanceof NullNode || rawValue == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying JsonNode.
|
||||
*/
|
||||
public JsonNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asText();
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package cz.moneta.test.harness.messaging;
|
||||
|
||||
/**
|
||||
* Content type of a received message.
|
||||
*/
|
||||
public enum MessageContentType {
|
||||
/**
|
||||
* JSON content - body is a JSON string.
|
||||
*/
|
||||
JSON,
|
||||
|
||||
/**
|
||||
* XML content - body is an XML string.
|
||||
*/
|
||||
XML,
|
||||
|
||||
/**
|
||||
* Raw text content - body is plain text (e.g., EBCDIC decoded, UTF-8).
|
||||
*/
|
||||
RAW_TEXT
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
package cz.moneta.test.harness.messaging;
|
||||
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
|
||||
/**
|
||||
* Response interface for received messages.
|
||||
* Provides assertion methods for verifying message content.
|
||||
* Shared interface for both Kafka and IBM MQ message responses.
|
||||
*/
|
||||
public interface MessageResponse {
|
||||
|
||||
/**
|
||||
* Assert that a field in the message body has the expected value.
|
||||
* For JSON: uses JSON path (dot/bracket notation).
|
||||
* For XML: uses XPath expression.
|
||||
*
|
||||
* @param path JSON path or XPath expression
|
||||
* @param value expected value as string
|
||||
* @return this instance for fluent assertions
|
||||
* @throws AssertionError if assertion fails
|
||||
*/
|
||||
MessageResponse andAssertFieldValue(String path, String value);
|
||||
|
||||
/**
|
||||
* Assert that a field exists in the message body.
|
||||
*
|
||||
* @param path JSON path or XPath expression
|
||||
* @return this instance for fluent assertions
|
||||
* @throws AssertionError if assertion fails
|
||||
*/
|
||||
MessageResponse andAssertPresent(String path);
|
||||
|
||||
/**
|
||||
* Assert that a field does not exist in the message body.
|
||||
*
|
||||
* @param path JSON path or XPath expression
|
||||
* @return this instance for fluent assertions
|
||||
* @throws AssertionError if assertion fails
|
||||
*/
|
||||
MessageResponse andAssertNotPresent(String path);
|
||||
|
||||
/**
|
||||
* Assert that a header (Kafka header or JMS property) has the expected value.
|
||||
*
|
||||
* @param headerName name of the header/property
|
||||
* @param value expected value
|
||||
* @return this instance for fluent assertions
|
||||
* @throws AssertionError if assertion fails
|
||||
*/
|
||||
MessageResponse andAssertHeaderValue(String headerName, String value);
|
||||
|
||||
/**
|
||||
* Assert that the message body contains a substring.
|
||||
* Primarily used for EBCDIC/UTF-8 raw text assertions.
|
||||
*
|
||||
* @param substring expected substring
|
||||
* @return this instance for fluent assertions
|
||||
* @throws AssertionError if assertion fails
|
||||
*/
|
||||
MessageResponse andAssertBodyContains(String substring);
|
||||
|
||||
/**
|
||||
* Get AssertJ fluent assertion for complex object assertions.
|
||||
*
|
||||
* @return AssertJ AbstractObjectAssert for fluent assertions
|
||||
*/
|
||||
AbstractObjectAssert<?, ?> andAssertWithAssertJ();
|
||||
|
||||
/**
|
||||
* Extract a value from the message body.
|
||||
* For JSON: uses JSON path (dot/bracket notation).
|
||||
* For XML: uses XPath expression.
|
||||
*
|
||||
* @param path JSON path or XPath expression
|
||||
* @return JsonPathValue wrapper for the extracted value
|
||||
*/
|
||||
JsonPathValue extract(String path);
|
||||
|
||||
/**
|
||||
* Deserialize the message body to a Java object.
|
||||
* For JSON: uses Jackson ObjectMapper.
|
||||
* For XML: uses JAXB or Jackson XmlMapper.
|
||||
*
|
||||
* @param type target type
|
||||
* @param <T> target type
|
||||
* @return deserialized object
|
||||
*/
|
||||
<T> T mapTo(Class<T> type);
|
||||
|
||||
/**
|
||||
* Get the raw message body.
|
||||
*
|
||||
* @return message body as string
|
||||
*/
|
||||
String getBody();
|
||||
|
||||
/**
|
||||
* Get a header value (Kafka header or JMS property).
|
||||
*
|
||||
* @param name header/property name
|
||||
* @return header value or null if not present
|
||||
*/
|
||||
String getHeader(String name);
|
||||
|
||||
/**
|
||||
* Get the underlying received message.
|
||||
*
|
||||
* @return ReceivedMessage instance
|
||||
*/
|
||||
ReceivedMessage getMessage();
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package cz.moneta.test.harness.messaging;
|
||||
|
||||
/**
|
||||
* Message format for IBM MQ.
|
||||
* Defines how messages are encoded and transmitted.
|
||||
*/
|
||||
public enum MqMessageFormat {
|
||||
/**
|
||||
* JSON format - JMS TextMessage with plain JSON string.
|
||||
* Default format for IBM MQ.
|
||||
*/
|
||||
JSON,
|
||||
|
||||
/**
|
||||
* XML format - JMS TextMessage with XML string.
|
||||
* XML is decoded and can be queried using XPath.
|
||||
*/
|
||||
XML,
|
||||
|
||||
/**
|
||||
* EBCDIC format - JMS BytesMessage with EBCDIC IBM-870 encoding.
|
||||
* Used for mainframe systems (Czech/Slovak characters).
|
||||
*/
|
||||
EBCDIC_870,
|
||||
|
||||
/**
|
||||
* UTF-8 format - JMS BytesMessage with UTF-8 (CCSID 1208) encoding.
|
||||
* Used for binary data with explicit UTF-8 encoding.
|
||||
*/
|
||||
UTF8_1208
|
||||
}
|
||||
@ -1,386 +0,0 @@
|
||||
package cz.moneta.test.harness.messaging;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Represents a received message from a messaging system.
|
||||
* Body is always normalized to a String regardless of source and wire format.
|
||||
* <p>
|
||||
* For Kafka: Avro GenericRecord is automatically converted to JSON.
|
||||
* For IBM MQ (JSON): JSON string from JMS TextMessage.
|
||||
* For IBM MQ (XML): XML string from JMS TextMessage.
|
||||
* For IBM MQ (EBCDIC): byte[] from JMS BytesMessage decoded from IBM-870.
|
||||
*/
|
||||
public class ReceivedMessage {
|
||||
|
||||
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
|
||||
private static final Pattern JSON_PATH_PATTERN = Pattern.compile("^([\\w.]+)\\[([\\d]+)\\](.*)$");
|
||||
|
||||
private final String body;
|
||||
private final MessageContentType contentType;
|
||||
private final Map<String, String> headers;
|
||||
private final long timestamp;
|
||||
private final String source;
|
||||
private final String key;
|
||||
|
||||
public ReceivedMessage(String body, MessageContentType contentType, Map<String, String> headers,
|
||||
long timestamp, String source, String key) {
|
||||
this.body = body;
|
||||
this.contentType = contentType;
|
||||
this.headers = headers != null ? Collections.unmodifiableMap(new HashMap<>(headers)) : new HashMap<>();
|
||||
this.timestamp = timestamp;
|
||||
this.source = source;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a JSON value using JSON path (dot/bracket notation).
|
||||
* Supports paths like "items[0].sku" or "nested.field".
|
||||
*
|
||||
* @param path JSON path
|
||||
* @return JsonNode for the extracted value
|
||||
*/
|
||||
public JsonNode extractJson(String path) {
|
||||
if (body == null || StringUtils.isEmpty(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode root = JSON_MAPPER.readTree(body);
|
||||
return evaluateJsonPath(root, path);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to extract JSON path: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a value using XPath (for XML messages).
|
||||
*
|
||||
* @param xpathExpression XPath expression
|
||||
* @return extracted value as string
|
||||
*/
|
||||
public String extractXml(String xpathExpression) {
|
||||
if (body == null || StringUtils.isEmpty(xpathExpression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
javax.xml.parsers.DocumentBuilder finalBuilder = builder;
|
||||
|
||||
var document = finalBuilder.parse(new java.io.ByteArrayInputStream(body.getBytes()));
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
Object result = xpath.evaluate(xpathExpression, document, XPathConstants.NODE);
|
||||
|
||||
if (result instanceof org.w3c.dom.Node domNode) {
|
||||
return domNode.getTextContent();
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to evaluate XPath: " + xpathExpression, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Universal extract method - auto-detects content type and uses appropriate extraction.
|
||||
*
|
||||
* @param expression JSON path or XPath expression
|
||||
* @return extracted value as string
|
||||
*/
|
||||
public String extract(String expression) {
|
||||
return switch (contentType) {
|
||||
case JSON -> extractJson(expression).asText();
|
||||
case XML -> extractXml(expression);
|
||||
case RAW_TEXT -> body;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate JSON path on a JSON node.
|
||||
* Supports dot notation and bracket notation for arrays.
|
||||
*/
|
||||
private JsonNode evaluateJsonPath(JsonNode node, String path) {
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
String[] parts = tokenizePath(path);
|
||||
JsonNode current = node;
|
||||
|
||||
for (String part : parts) {
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part.contains("[")) {
|
||||
// Array access
|
||||
Matcher matcher = JSON_PATH_PATTERN.matcher(part);
|
||||
if (matcher.matches()) {
|
||||
String arrayName = matcher.group(1);
|
||||
int index = Integer.parseInt(matcher.group(2));
|
||||
String remaining = matcher.group(3);
|
||||
|
||||
if (current.isArray()) {
|
||||
current = index < current.size() ? current.get(index) : null;
|
||||
} else if (current.isObject()) {
|
||||
JsonNode arrayNode = current.get(arrayName);
|
||||
if (arrayNode != null && arrayNode.isArray()) {
|
||||
current = index < arrayNode.size() ? arrayNode.get(index) : null;
|
||||
} else {
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with remaining path
|
||||
if (StringUtils.isNotBlank(remaining)) {
|
||||
current = evaluateJsonPath(current, remaining);
|
||||
}
|
||||
} else {
|
||||
current = current.get(part);
|
||||
}
|
||||
} else if (part.contains(".")) {
|
||||
// Navigate through object properties
|
||||
String[] segments = part.split("\\.");
|
||||
for (String segment : segments) {
|
||||
if (StringUtils.isEmpty(segment)) {
|
||||
continue;
|
||||
}
|
||||
current = current.get(segment);
|
||||
}
|
||||
} else {
|
||||
current = current.get(part);
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenize JSON path into segments.
|
||||
*/
|
||||
private String[] tokenizePath(String path) {
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
java.util.List<String> tokens = new java.util.ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
boolean inBracket = false;
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
char c = path.charAt(i);
|
||||
if (c == '[') {
|
||||
inBracket = true;
|
||||
current.append(c);
|
||||
} else if (c == ']') {
|
||||
inBracket = false;
|
||||
current.append(c);
|
||||
} else if (c == '.' && !inBracket) {
|
||||
if (current.length() > 0) {
|
||||
tokens.add(current.toString());
|
||||
current.setLength(0);
|
||||
}
|
||||
} else {
|
||||
current.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (current.length() > 0) {
|
||||
tokens.add(current.toString());
|
||||
}
|
||||
|
||||
return tokens.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message key (Kafka message key, null for IBM MQ).
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a header value (Kafka header or JMS property).
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers.
|
||||
*/
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message body.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message timestamp.
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content type.
|
||||
*/
|
||||
public MessageContentType getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source (topic or queue name).
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the message body to a Java object.
|
||||
*
|
||||
* @param type target type
|
||||
* @param <T> target type
|
||||
* @return deserialized object
|
||||
*/
|
||||
public <T> T mapTo(Class<T> type) {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (contentType == MessageContentType.XML) {
|
||||
// XML deserialization using JAXB
|
||||
return mapXmlTo(type);
|
||||
} else {
|
||||
// JSON deserialization using Jackson
|
||||
return JSON_MAPPER.readValue(body, type);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to deserialize message to " + type.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the message body to a Java object for XML.
|
||||
* Uses JAXB-like parsing - simplified for basic XML structures.
|
||||
*/
|
||||
private <T> T mapXmlTo(Class<T> type) {
|
||||
try {
|
||||
// For XML, parse to a simple map structure
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
javax.xml.parsers.DocumentBuilder finalBuilder = builder;
|
||||
|
||||
var document = finalBuilder.parse(new java.io.ByteArrayInputStream(body.getBytes()));
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
Map<String, Object> xmlMap = xmlToMap(document.getDocumentElement());
|
||||
return JSON_MAPPER.convertValue(xmlMap, type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to deserialize XML message", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert XML element to Map.
|
||||
*/
|
||||
private Map<String, Object> xmlToMap(org.w3c.dom.Element element) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < element.getAttributes().getLength(); i++) {
|
||||
org.w3c.dom.NamedNodeMap attributes = element.getAttributes();
|
||||
org.w3c.dom.Node attr = attributes.item(i);
|
||||
result.put("@" + attr.getNodeName(), attr.getNodeValue());
|
||||
}
|
||||
|
||||
// Add children
|
||||
for (int i = 0; i < element.getChildNodes().getLength(); i++) {
|
||||
org.w3c.dom.Node node = element.getChildNodes().item(i);
|
||||
if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
|
||||
org.w3c.dom.Element childElement = (org.w3c.dom.Element) node;
|
||||
String tagName = childElement.getTagName();
|
||||
|
||||
if (childElement.getChildNodes().getLength() == 0) {
|
||||
// Leaf element
|
||||
result.put(tagName, childElement.getTextContent());
|
||||
} else {
|
||||
// Check if all children are elements (complex) or text (simple)
|
||||
boolean hasElement = false;
|
||||
for (int j = 0; j < childElement.getChildNodes().getLength(); j++) {
|
||||
org.w3c.dom.Node childNode = childElement.getChildNodes().item(j);
|
||||
if (childNode.getNodeType() == org.w3c.dom.Node.TEXT_NODE &&
|
||||
StringUtils.isNotBlank(childNode.getTextContent())) {
|
||||
} else if (childNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
|
||||
hasElement = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasElement) {
|
||||
Map<String, Object> childMap = xmlToMap(childElement);
|
||||
if (result.containsKey(tagName)) {
|
||||
// Convert to list if multiple elements with same name
|
||||
java.util.List<Object> list = new java.util.ArrayList<>();
|
||||
if (result.get(tagName) instanceof Map) {
|
||||
list.add(result.get(tagName));
|
||||
}
|
||||
list.add(childMap);
|
||||
result.put(tagName, list);
|
||||
} else {
|
||||
result.put(tagName, childMap);
|
||||
}
|
||||
} else {
|
||||
result.put(tagName, childElement.getTextContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If element has only text content and no attributes or children, return text
|
||||
if (element.getChildNodes().getLength() == 0) {
|
||||
Map<String, Object> textMap = new HashMap<>();
|
||||
textMap.put("#text", element.getTextContent());
|
||||
return textMap;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReceivedMessage{" +
|
||||
"contentType=" + contentType +
|
||||
", source='" + source + '\'' +
|
||||
", key='" + key + '\'' +
|
||||
", body='" + body + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -5,10 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionEndpoint;
|
||||
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionQueue;
|
||||
import cz.moneta.test.harness.exception.HarnessException;
|
||||
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.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.messaging.exception.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.util.FileReader;
|
||||
import cz.moneta.test.harness.support.util.Template;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -381,7 +378,7 @@ public final class ImqRequest {
|
||||
|
||||
@Override
|
||||
public MessageResponse withTimeout(long duration, java.util.concurrent.TimeUnit unit) {
|
||||
return withTimeout(Duration.of(duration, java.time.temporal.ChronoUnit.MILLIS));
|
||||
return withTimeout(Duration.of(duration, unit.toChronoUnit()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -556,8 +553,8 @@ public final class ImqRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public cz.moneta.test.harness.support.messaging.ReceivedMessage getMessage() {
|
||||
return cz.moneta.test.harness.support.messaging.ReceivedMessage.fromMessagingReceivedMessage(message);
|
||||
public ReceivedMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -5,15 +5,17 @@ package cz.moneta.test.harness.support.messaging;
|
||||
*/
|
||||
public enum MessageContentType {
|
||||
/**
|
||||
* JSON content - body is a JSON string, can use dot-path or bracket notation for extraction
|
||||
* JSON content - body is a JSON string.
|
||||
*/
|
||||
JSON,
|
||||
|
||||
/**
|
||||
* XML content - body is an XML string, can use XPath for extraction
|
||||
* XML content - body is an XML string.
|
||||
*/
|
||||
XML,
|
||||
|
||||
/**
|
||||
* Raw text content - body is plain text without structured format
|
||||
* Raw text content - body is plain text (e.g., EBCDIC decoded, UTF-8).
|
||||
*/
|
||||
RAW_TEXT
|
||||
}
|
||||
|
||||
@ -1,23 +1,31 @@
|
||||
package cz.moneta.test.harness.support.messaging;
|
||||
|
||||
/**
|
||||
* Message format for IBM MQ messages.
|
||||
* Message format for IBM MQ.
|
||||
* Defines how messages are encoded and transmitted.
|
||||
*/
|
||||
public enum MqMessageFormat {
|
||||
/**
|
||||
* JSON format - JMS TextMessage with plain JSON string (UTF-8 default)
|
||||
* JSON format - MQ string message with plain JSON string.
|
||||
* Default format for IBM MQ.
|
||||
*/
|
||||
JSON,
|
||||
|
||||
/**
|
||||
* XML format - JMS TextMessage with XML string (UTF-8 default)
|
||||
* XML format - MQ string message with XML string.
|
||||
* XML is decoded and can be queried using XPath.
|
||||
*/
|
||||
XML,
|
||||
|
||||
/**
|
||||
* EBCDIC format - JMS BytesMessage with EBCDIC IBM-870 encoding (CZ/SK mainframe)
|
||||
* EBCDIC format - MQ bytes message with EBCDIC IBM-870 encoding.
|
||||
* Used for mainframe systems (Czech/Slovak characters).
|
||||
*/
|
||||
EBCDIC_870,
|
||||
|
||||
/**
|
||||
* UTF-8 format - JMS BytesMessage with UTF-8 IBM CCSID 1208 encoding
|
||||
* UTF-8 format - MQ bytes message with UTF-8 (CCSID 1208) encoding.
|
||||
* Used for binary data with explicit UTF-8 encoding.
|
||||
*/
|
||||
UTF8_1208
|
||||
}
|
||||
|
||||
@ -2,36 +2,32 @@ package cz.moneta.test.harness.support.messaging;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Represents a received message from a messaging system (IBM MQ or Kafka).
|
||||
* Represents a received message from a messaging system.
|
||||
* Body is always normalized to a String regardless of source and wire format.
|
||||
* <p>
|
||||
* Provides unified API for accessing message content regardless of source system.
|
||||
* Body is always normalized to a String, with content type detection for proper extraction.
|
||||
* </p>
|
||||
* For Kafka: Avro GenericRecord is automatically converted to JSON.
|
||||
* For IBM MQ (JSON): JSON string from MQ string message.
|
||||
* For IBM MQ (XML): XML string from MQ string message.
|
||||
* For IBM MQ (EBCDIC): byte[] from MQ message decoded from IBM-870.
|
||||
*/
|
||||
public class ReceivedMessage {
|
||||
|
||||
private static final ObjectMapper JSON_MAPPER = new ObjectMapper();
|
||||
private static final XmlMapper XML_MAPPER = new XmlMapper();
|
||||
private static final Pattern JSON_PATH_PATTERN = Pattern.compile("^([\\w.]+)\\[([\\d]+)\\](.*)$");
|
||||
|
||||
private final String body;
|
||||
private final MessageContentType contentType;
|
||||
@ -40,80 +36,75 @@ public class ReceivedMessage {
|
||||
private final String source;
|
||||
private final String key;
|
||||
|
||||
private ReceivedMessage(Builder builder) {
|
||||
this.body = builder.body;
|
||||
this.contentType = builder.contentType;
|
||||
this.headers = builder.headers != null ? Collections.unmodifiableMap(new HashMap<>(builder.headers)) : Collections.emptyMap();
|
||||
this.timestamp = builder.timestamp;
|
||||
this.source = builder.source;
|
||||
this.key = builder.key;
|
||||
public ReceivedMessage(String body, MessageContentType contentType, Map<String, String> headers,
|
||||
long timestamp, String source, String key) {
|
||||
this.body = body;
|
||||
this.contentType = contentType;
|
||||
this.headers = headers != null ? Collections.unmodifiableMap(new HashMap<>(headers)) : new HashMap<>();
|
||||
this.timestamp = timestamp;
|
||||
this.source = source;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder for ReceivedMessage.
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a value from the message body using JSON dot-path or bracket notation.
|
||||
* <p>
|
||||
* Supports paths like:
|
||||
* <ul>
|
||||
* <li>{@code "field"} - top-level field</li>
|
||||
* <li>{@code "parent.child"} - nested field</li>
|
||||
* <li>{@code "items[0]"} - array element</li>
|
||||
* <li>{@code "items[0].name"} - nested field in array element</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* Extract a JSON value using JSON path (dot/bracket notation).
|
||||
* Supports paths like "items[0].sku" or "nested.field".
|
||||
*
|
||||
* @param path the JSON path expression
|
||||
* @return the extracted value as JsonNode
|
||||
* @param path JSON path
|
||||
* @return JsonNode for the extracted value
|
||||
*/
|
||||
public JsonNode extractJson(String path) {
|
||||
if (contentType != MessageContentType.JSON) {
|
||||
throw new IllegalStateException("JSON extraction is only supported for JSON content type, got: " + contentType);
|
||||
if (body == null || StringUtils.isEmpty(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JsonNode rootNode = JSON_MAPPER.readTree(body);
|
||||
return extractNode(path, rootNode);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to parse JSON body: " + body, e);
|
||||
JsonNode root = JSON_MAPPER.readTree(body);
|
||||
return evaluateJsonPath(root, path);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to extract JSON path: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a value from XML message body using XPath expression.
|
||||
* Extract a value using XPath (for XML messages).
|
||||
*
|
||||
* @param xpath the XPath expression
|
||||
* @return the extracted value as String
|
||||
* @param xpathExpression XPath expression
|
||||
* @return extracted value as string
|
||||
*/
|
||||
public String extractXml(String xpath) {
|
||||
if (contentType != MessageContentType.XML) {
|
||||
throw new IllegalStateException("XML extraction is only supported for XML content type, got: " + contentType);
|
||||
public String extractXml(String xpathExpression) {
|
||||
if (body == null || StringUtils.isEmpty(xpathExpression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document doc = builder.parse(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
|
||||
XPath xPath = XPathFactory.newInstance().newXPath();
|
||||
return xPath.evaluate(xpath, doc);
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new RuntimeException("Failed to evaluate XPath: " + xpath, e);
|
||||
javax.xml.parsers.DocumentBuilder finalBuilder = builder;
|
||||
|
||||
var document = finalBuilder.parse(new java.io.ByteArrayInputStream(body.getBytes()));
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
Object result = xpath.evaluate(xpathExpression, document, XPathConstants.NODE);
|
||||
|
||||
if (result instanceof org.w3c.dom.Node domNode) {
|
||||
return domNode.getTextContent();
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to parse XML body: " + body, e);
|
||||
throw new RuntimeException("Failed to evaluate XPath: " + xpathExpression, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a value from the message body. Auto-detects content type and uses appropriate extraction method.
|
||||
* Universal extract method - auto-detects content type and uses appropriate extraction.
|
||||
*
|
||||
* @param expression the path expression (JSON path for JSON, XPath for XML)
|
||||
* @return the extracted value as String
|
||||
* @param expression JSON path or XPath expression
|
||||
* @return extracted value as string
|
||||
*/
|
||||
public String extract(String expression) {
|
||||
return switch (contentType) {
|
||||
@ -124,181 +115,272 @@ public class ReceivedMessage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message body as a String.
|
||||
*
|
||||
* @return the body content
|
||||
* Evaluate JSON path on a JSON node.
|
||||
* Supports dot notation and bracket notation for arrays.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
private JsonNode evaluateJsonPath(JsonNode node, String path) {
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
String[] parts = tokenizePath(path);
|
||||
JsonNode current = node;
|
||||
|
||||
for (String part : parts) {
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (part.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part.contains("[")) {
|
||||
// Array access
|
||||
Matcher matcher = JSON_PATH_PATTERN.matcher(part);
|
||||
if (matcher.matches()) {
|
||||
String arrayName = matcher.group(1);
|
||||
int index = Integer.parseInt(matcher.group(2));
|
||||
String remaining = matcher.group(3);
|
||||
|
||||
if (current.isArray()) {
|
||||
current = index < current.size() ? current.get(index) : null;
|
||||
} else if (current.isObject()) {
|
||||
JsonNode arrayNode = current.get(arrayName);
|
||||
if (arrayNode != null && arrayNode.isArray()) {
|
||||
current = index < arrayNode.size() ? arrayNode.get(index) : null;
|
||||
} else {
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with remaining path
|
||||
if (StringUtils.isNotBlank(remaining)) {
|
||||
current = evaluateJsonPath(current, remaining);
|
||||
}
|
||||
} else {
|
||||
current = current.get(part);
|
||||
}
|
||||
} else if (part.contains(".")) {
|
||||
// Navigate through object properties
|
||||
String[] segments = part.split("\\.");
|
||||
for (String segment : segments) {
|
||||
if (StringUtils.isEmpty(segment)) {
|
||||
continue;
|
||||
}
|
||||
current = current.get(segment);
|
||||
}
|
||||
} else {
|
||||
current = current.get(part);
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message content type.
|
||||
*
|
||||
* @return the content type
|
||||
* Tokenize JSON path into segments.
|
||||
*/
|
||||
public MessageContentType getContentType() {
|
||||
return contentType;
|
||||
private String[] tokenizePath(String path) {
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
java.util.List<String> tokens = new java.util.ArrayList<>();
|
||||
StringBuilder current = new StringBuilder();
|
||||
boolean inBracket = false;
|
||||
|
||||
for (int i = 0; i < path.length(); i++) {
|
||||
char c = path.charAt(i);
|
||||
if (c == '[') {
|
||||
inBracket = true;
|
||||
current.append(c);
|
||||
} else if (c == ']') {
|
||||
inBracket = false;
|
||||
current.append(c);
|
||||
} else if (c == '.' && !inBracket) {
|
||||
if (current.length() > 0) {
|
||||
tokens.add(current.toString());
|
||||
current.setLength(0);
|
||||
}
|
||||
} else {
|
||||
current.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (current.length() > 0) {
|
||||
tokens.add(current.toString());
|
||||
}
|
||||
|
||||
return tokens.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header value by name.
|
||||
*
|
||||
* @param name the header name
|
||||
* @return the header value, or null if not present
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all headers.
|
||||
*
|
||||
* @return unmodifiable map of headers
|
||||
*/
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message timestamp.
|
||||
*
|
||||
* @return timestamp in milliseconds
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source (topic name for Kafka, queue name for IBM MQ).
|
||||
*
|
||||
* @return the source name
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message key (Kafka only, null for IBM MQ).
|
||||
*
|
||||
* @return the message key or null
|
||||
* Get the message key (Kafka message key, null for IBM MQ).
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the message body into a Java object.
|
||||
* <p>
|
||||
* For JSON content: uses Jackson ObjectMapper.readValue
|
||||
* For XML content: uses Jackson XmlMapper
|
||||
* </p>
|
||||
* Get a header value (Kafka header or JMS property).
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all headers.
|
||||
*/
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message body.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message timestamp.
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content type.
|
||||
*/
|
||||
public MessageContentType getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source (topic or queue name).
|
||||
*/
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the message body to a Java object.
|
||||
*
|
||||
* @param type the target class
|
||||
* @param <T> the target type
|
||||
* @return the deserialized object
|
||||
* @param type target type
|
||||
* @param <T> target type
|
||||
* @return deserialized object
|
||||
*/
|
||||
public <T> T mapTo(Class<T> type) {
|
||||
try {
|
||||
if (contentType == MessageContentType.XML) {
|
||||
return XML_MAPPER.readValue(body, type);
|
||||
} else {
|
||||
return JSON_MAPPER.readValue(body, type);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to deserialize message body to " + type.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for ReceivedMessage.
|
||||
*/
|
||||
public static class Builder {
|
||||
private String body;
|
||||
private MessageContentType contentType = MessageContentType.JSON;
|
||||
private Map<String, String> headers;
|
||||
private long timestamp = System.currentTimeMillis();
|
||||
private String source;
|
||||
private String key;
|
||||
|
||||
public Builder body(String body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder contentType(MessageContentType contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder headers(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder timestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder source(String source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder key(String key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReceivedMessage build() {
|
||||
return new ReceivedMessage(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a node from JSON using dot/bracket notation.
|
||||
*/
|
||||
private static JsonNode extractNode(String path, JsonNode rootNode) {
|
||||
Pattern arrayPattern = Pattern.compile("(.*?)\\[([0-9]+)\\]");
|
||||
return Arrays.stream(path.split("\\."))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.reduce(rootNode,
|
||||
(node, part) -> {
|
||||
Matcher matcher = arrayPattern.matcher(part);
|
||||
if (matcher.find()) {
|
||||
return node.path(matcher.group(1)).path(Integer.parseInt(matcher.group(2)));
|
||||
} else {
|
||||
return node.path(part);
|
||||
}
|
||||
},
|
||||
(n1, n2) -> n1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a cz.moneta.test.harness.messaging.ReceivedMessage to this class.
|
||||
*
|
||||
* @param other the message to convert
|
||||
* @return converted message
|
||||
*/
|
||||
public static ReceivedMessage fromMessagingReceivedMessage(
|
||||
cz.moneta.test.harness.messaging.ReceivedMessage other) {
|
||||
if (other == null) {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
cz.moneta.test.harness.support.messaging.MessageContentType contentType =
|
||||
switch (other.getContentType()) {
|
||||
case JSON -> cz.moneta.test.harness.support.messaging.MessageContentType.JSON;
|
||||
case XML -> cz.moneta.test.harness.support.messaging.MessageContentType.XML;
|
||||
case RAW_TEXT -> cz.moneta.test.harness.support.messaging.MessageContentType.RAW_TEXT;
|
||||
};
|
||||
return builder()
|
||||
.body(other.getBody())
|
||||
.contentType(contentType)
|
||||
.headers(other.getHeaders())
|
||||
.timestamp(other.getTimestamp())
|
||||
.source(other.getSource())
|
||||
.key(other.getKey())
|
||||
.build();
|
||||
|
||||
try {
|
||||
if (contentType == MessageContentType.XML) {
|
||||
// XML deserialization using JAXB
|
||||
return mapXmlTo(type);
|
||||
} else {
|
||||
// JSON deserialization using Jackson
|
||||
return JSON_MAPPER.readValue(body, type);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to deserialize message to " + type.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the message body to a Java object for XML.
|
||||
* Uses JAXB-like parsing - simplified for basic XML structures.
|
||||
*/
|
||||
private <T> T mapXmlTo(Class<T> type) {
|
||||
try {
|
||||
// For XML, parse to a simple map structure
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
javax.xml.parsers.DocumentBuilder finalBuilder = builder;
|
||||
|
||||
var document = finalBuilder.parse(new java.io.ByteArrayInputStream(body.getBytes()));
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
Map<String, Object> xmlMap = xmlToMap(document.getDocumentElement());
|
||||
return JSON_MAPPER.convertValue(xmlMap, type);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to deserialize XML message", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert XML element to Map.
|
||||
*/
|
||||
private Map<String, Object> xmlToMap(org.w3c.dom.Element element) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
for (int i = 0; i < element.getAttributes().getLength(); i++) {
|
||||
org.w3c.dom.NamedNodeMap attributes = element.getAttributes();
|
||||
org.w3c.dom.Node attr = attributes.item(i);
|
||||
result.put("@" + attr.getNodeName(), attr.getNodeValue());
|
||||
}
|
||||
|
||||
// Add children
|
||||
for (int i = 0; i < element.getChildNodes().getLength(); i++) {
|
||||
org.w3c.dom.Node node = element.getChildNodes().item(i);
|
||||
if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
|
||||
org.w3c.dom.Element childElement = (org.w3c.dom.Element) node;
|
||||
String tagName = childElement.getTagName();
|
||||
|
||||
if (childElement.getChildNodes().getLength() == 0) {
|
||||
// Leaf element
|
||||
result.put(tagName, childElement.getTextContent());
|
||||
} else {
|
||||
// Check if all children are elements (complex) or text (simple)
|
||||
boolean hasElement = false;
|
||||
for (int j = 0; j < childElement.getChildNodes().getLength(); j++) {
|
||||
org.w3c.dom.Node childNode = childElement.getChildNodes().item(j);
|
||||
if (childNode.getNodeType() == org.w3c.dom.Node.TEXT_NODE &&
|
||||
StringUtils.isNotBlank(childNode.getTextContent())) {
|
||||
} else if (childNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
|
||||
hasElement = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasElement) {
|
||||
Map<String, Object> childMap = xmlToMap(childElement);
|
||||
if (result.containsKey(tagName)) {
|
||||
// Convert to list if multiple elements with same name
|
||||
java.util.List<Object> list = new java.util.ArrayList<>();
|
||||
if (result.get(tagName) instanceof Map) {
|
||||
list.add(result.get(tagName));
|
||||
}
|
||||
list.add(childMap);
|
||||
result.put(tagName, list);
|
||||
} else {
|
||||
result.put(tagName, childMap);
|
||||
}
|
||||
} else {
|
||||
result.put(tagName, childElement.getTextContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If element has only text content and no attributes or children, return text
|
||||
if (element.getChildNodes().getLength() == 0) {
|
||||
Map<String, Object> textMap = new HashMap<>();
|
||||
textMap.put("#text", element.getTextContent());
|
||||
return textMap;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReceivedMessage{" +
|
||||
"contentType=" + contentType +
|
||||
", source='" + source + '\'' +
|
||||
", key='" + key + '\'' +
|
||||
", body='" + body + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package cz.moneta.test.harness.messaging.exception;
|
||||
package cz.moneta.test.harness.support.messaging.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when connection to messaging system fails.
|
||||
@ -1,4 +1,4 @@
|
||||
package cz.moneta.test.harness.messaging.exception;
|
||||
package cz.moneta.test.harness.support.messaging.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when destination (topic/queue) does not exist or is inaccessible.
|
||||
@ -1,4 +1,4 @@
|
||||
package cz.moneta.test.harness.messaging.exception;
|
||||
package cz.moneta.test.harness.support.messaging.exception;
|
||||
|
||||
/**
|
||||
* Base exception for all messaging-related errors.
|
||||
@ -1,4 +1,4 @@
|
||||
package cz.moneta.test.harness.messaging.exception;
|
||||
package cz.moneta.test.harness.support.messaging.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when schema validation fails or schema is not found.
|
||||
@ -1,4 +1,4 @@
|
||||
package cz.moneta.test.harness.messaging.exception;
|
||||
package cz.moneta.test.harness.support.messaging.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when message receiving times out.
|
||||
@ -1,25 +1,19 @@
|
||||
package cz.moneta.test.harness.support.messaging.kafka;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import cz.moneta.test.harness.endpoints.kafka.KafkaEndpoint;
|
||||
import cz.moneta.test.harness.support.util.FileReader;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Fluent builder for creating and sending Kafka messages.
|
||||
@ -69,6 +63,8 @@ public class KafkaRequest {
|
||||
* @return KafkaReceiveFilterPhase for filter configuration
|
||||
*/
|
||||
KafkaReceiveFilterPhase fromTopic(String topic);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,6 +183,20 @@ public class KafkaRequest {
|
||||
* Phase 2b: Configure filter for receiving.
|
||||
*/
|
||||
public interface KafkaReceiveFilterPhase {
|
||||
/**
|
||||
* Specifies the starting point of consumption at the start of partitions.
|
||||
*
|
||||
* @return KafkaReceiveFilterPhase for filter configuration
|
||||
*/
|
||||
KafkaReceiveFilterPhase startFromBeginning();
|
||||
|
||||
/**
|
||||
* Specifies the starting point of consumption at the end of partitions
|
||||
*
|
||||
* @return KafkaReceiveFilterPhase for filter configuration
|
||||
*/
|
||||
KafkaReceiveFilterPhase startFromEnd();
|
||||
|
||||
/**
|
||||
* Specifies the filter predicate for receiving messages.
|
||||
*
|
||||
@ -229,7 +239,8 @@ public class KafkaRequest {
|
||||
// Receive configuration
|
||||
private Predicate<ReceivedMessage> filter;
|
||||
private Duration timeout;
|
||||
|
||||
//default true
|
||||
private boolean startFromBeginning = true;
|
||||
// Response (after receive)
|
||||
private List<ReceivedMessage> messages;
|
||||
|
||||
@ -363,6 +374,18 @@ public class KafkaRequest {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KafkaReceiveFilterPhase startFromBeginning() {
|
||||
this.startFromBeginning = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KafkaReceiveFilterPhase startFromEnd() {
|
||||
this.startFromBeginning = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KafkaAwaitingPhase receiveWhere(Predicate<ReceivedMessage> filter) {
|
||||
this.filter = filter;
|
||||
@ -372,7 +395,7 @@ public class KafkaRequest {
|
||||
@Override
|
||||
public MessageResponse withTimeout(long duration, TimeUnit unit) {
|
||||
this.timeout = Duration.of(duration, unit.toChronoUnit());
|
||||
messages = endpoint.receive(topic, filter, timeout);
|
||||
messages = endpoint.receive(topic, filter, timeout, startFromBeginning);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
BIN
test-harness/src/main/resources/keystores/imq.jks
Normal file
BIN
test-harness/src/main/resources/keystores/imq.jks
Normal file
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
|
||||
Hello World!!!
|
||||
Hello World Too!!!
|
||||
# Automated tests
|
||||
|
||||
This repo contains automated tests for all squads.
|
||||
|
||||
@ -73,24 +73,6 @@
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/lib</outputDirectory>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
|
||||
@ -22,6 +22,7 @@ import cz.moneta.test.dsl.fortelight.ForteLight;
|
||||
import cz.moneta.test.dsl.greenscreen.GreenScreen;
|
||||
import cz.moneta.test.dsl.hypos.Hypos;
|
||||
import cz.moneta.test.dsl.ib.Ib;
|
||||
import cz.moneta.test.dsl.payment_engine_mq.PaeMqBuilder;
|
||||
import cz.moneta.test.dsl.ilods.Ilods;
|
||||
import cz.moneta.test.dsl.imq.ImqFirstVision;
|
||||
import cz.moneta.test.dsl.kafka.Kafka;
|
||||
@ -142,7 +143,6 @@ public class Harness extends BaseStoreAccessor {
|
||||
return new Ufo(this, SECOND_BANKER_TOKEN);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Oauth2Web withOauth2Web() {
|
||||
return new Oauth2Web(this);
|
||||
}
|
||||
@ -191,6 +191,10 @@ public class Harness extends BaseStoreAccessor {
|
||||
return new AresApiBuilder(this);
|
||||
}
|
||||
|
||||
public PaeMqBuilder withPaeMq() {
|
||||
return new PaeMqBuilder(this);
|
||||
}
|
||||
|
||||
public Sb withSB() {
|
||||
return new Sb(this);
|
||||
}
|
||||
|
||||
@ -152,6 +152,7 @@ public interface CustomerPage extends SmartAutoFlow<CustomerPage>, StoreAccessor
|
||||
@TypeInto(NAME)
|
||||
CustomerPage typeName(String name);
|
||||
|
||||
@Wait(value = PHONE_NUMBER, until = Until.VISIBLE)
|
||||
@TypeInto(PHONE_NUMBER)
|
||||
CustomerPage typePhoneNumber(String phoneNumber);
|
||||
|
||||
@ -331,6 +332,7 @@ public interface CustomerPage extends SmartAutoFlow<CustomerPage>, StoreAccessor
|
||||
@Click(value = STAY_TYPE_CHOOSE, jsClick = true)
|
||||
CustomerPage chooseStayType(StayType stayType);
|
||||
|
||||
@Wait(value = BUSINESSMAN_PHONE_NUMBER, until = Until.VISIBLE)
|
||||
@TypeInto(BUSINESSMAN_PHONE_NUMBER)
|
||||
CustomerPage typeBusinessmanPhoneNumber(String phoneNumber);
|
||||
|
||||
@ -355,6 +357,7 @@ public interface CustomerPage extends SmartAutoFlow<CustomerPage>, StoreAccessor
|
||||
@Click(NUMBER_OF_EMPLOYEES_FIELD)
|
||||
CustomerPage chooseNoOfEmployees();
|
||||
|
||||
@Wait (value = BUSINESSMAN_SILENT_COMPANION, until = Until.CLICKABLE)
|
||||
@Click(BUSINESSMAN_SILENT_COMPANION)
|
||||
CustomerPage clickBusinessmanSilentCompanion();
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ public interface NewCalculationPage extends SmartAutoFlow<NewCalculationPage>, S
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_BIRTHDATE = "//label//span[contains(text(), 'Datum narození')]/../following-sibling::div/input";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_PIN = "//label//span[contains(text(), 'Rodné číslo')]/../following-sibling::input";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY = "//label/span/span[contains(text(), 'Občanství')]/../../following-sibling::div/div/div/input";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_TEXTBOX = "//label/span/span[contains(text(), 'Občanství')]/../../../div/div/div";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_FIELD = "//label/span/span[contains(text(), 'Občanství')]/../../following-sibling::div/div/button[1]";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_ADDRESS = "//span[contains(text(),'Trvalé bydliště')]/../../following-sibling::div/div/div/input";
|
||||
String VEHICLE_CAR_INSURANCE_CUSTOMER_ADDRESS_FIELDS = "//span[contains(text(),'Trvalé bydliště')]/../../following-sibling::div/div/button[1]";
|
||||
@ -253,8 +254,12 @@ public interface NewCalculationPage extends SmartAutoFlow<NewCalculationPage>, S
|
||||
@TypeInto(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY, clear = true)
|
||||
NewCalculationPage typeVehicleInsuranceCustomerNationality(String nationality);
|
||||
|
||||
@Wait(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_FIELD)
|
||||
@Click(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_FIELD, jsClick = true)
|
||||
@Wait(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_TEXTBOX)
|
||||
@Click(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_TEXTBOX, andWait = @Wait(explicitWaitSeconds = 1))
|
||||
NewCalculationPage clickVehicleInsuranceCustomerNationalityTextbox();
|
||||
|
||||
@Wait(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_FIELD, until = Until.VISIBLE)
|
||||
@Click(value = VEHICLE_CAR_INSURANCE_CUSTOMER_NATIONALITY_FIELD)
|
||||
NewCalculationPage clickVehicleInsuranceCustomerNationality();
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_CUSTOMER_ADDRESS)
|
||||
@ -271,19 +276,19 @@ public interface NewCalculationPage extends SmartAutoFlow<NewCalculationPage>, S
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_VEHICLE_TAB)
|
||||
@TypeInto(value = VEHICLE_CAR_INSURANCE_VEHICLE_VOLUME_AGRO)
|
||||
NewCalculationPage typeVehicleInsuranceVehicleVolume(String volume);
|
||||
NewCalculationPage typeVehicleInsuranceVehicleVolume(int volume);
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_VEHICLE_TAB)
|
||||
@TypeInto(value = VEHICLE_CAR_INSURANCE_VEHICLE_NUMBER_OF_SEATS)
|
||||
NewCalculationPage typeVehicleInsuranceVehicleNumberOfSeats(String numberOfSeats);
|
||||
NewCalculationPage typeVehicleInsuranceVehicleNumberOfSeats(int numberOfSeats);
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_VEHICLE_TAB)
|
||||
@TypeInto(value = VEHICLE_CAR_INSURANCE_VEHICLE_POWER_AGRO, andWait = @Wait(explicitWaitSeconds = 2))
|
||||
NewCalculationPage typeVehicleInsuranceVehiclePower(String power);
|
||||
NewCalculationPage typeVehicleInsuranceVehiclePower(int power);
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_VEHICLE_TAB)
|
||||
@TypeInto(value = VEHICLE_CAR_INSURANCE_VEHICLE_WEIGHT_AGRO)
|
||||
NewCalculationPage typeVehicleInsuranceVehicleWeight(String weight);
|
||||
NewCalculationPage typeVehicleInsuranceVehicleWeight(int weight);
|
||||
|
||||
@Wait(VEHICLE_CAR_INSURANCE_VEHICLE_TAB)
|
||||
@Click(VEHICLE_CAR_INSURANCE_VEHICLE_FUEL_AGRO)
|
||||
|
||||
@ -2,10 +2,7 @@ package cz.moneta.test.dsl.auto.smartauto;
|
||||
|
||||
import cz.moneta.test.dsl.auto.smartauto.shared.BankCode;
|
||||
import cz.moneta.test.dsl.auto.smartauto.shared.CommonElements;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.Click;
|
||||
import cz.moneta.test.harness.support.web.TypeInto;
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
public interface OthersPage extends SmartAutoFlow<OthersPage> {
|
||||
|
||||
@ -45,12 +42,14 @@ public interface OthersPage extends SmartAutoFlow<OthersPage> {
|
||||
@TypeInto(APPROVAL_MESSAGE)
|
||||
OthersPage typeApprovalMessage(String approvalMessage);
|
||||
|
||||
@Wait(value = VEHICLE_PAGE, until = Until.CLICKABLE)
|
||||
@Click(value = VEHICLE_PAGE, jsClick = true)
|
||||
VehiclePage clickVehiclePage();
|
||||
|
||||
@Click(value = CommonElements.SAVE_CALCULATION_BUTTON, andWait = @Wait(value = CommonElements.SUCCESSFUL_SAVED_CALCULATION_MESSAGE), jsClick = true)
|
||||
OthersPage clickSaveCalculation();
|
||||
|
||||
@Wait(value = DUPLICATE_APPLICATION, until = Until.CLICKABLE)
|
||||
@Click(value = DUPLICATE_APPLICATION, jsClick = true)
|
||||
OthersPage clickDuplicateApplication();
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@ import cz.moneta.test.harness.support.web.*;
|
||||
public interface SavedCalculationsPage extends SmartAutoFlow<SavedCalculationsPage>, StoreAccessor, CalendarAccessor {
|
||||
|
||||
String HEADING_SAVED_CALCULATIONS = "//h1[contains(text(), 'Kalkulace')]";
|
||||
String FIRST_APPLICATION_DIV = "/html/body/div[1]/div[1]/div/div/div[1]/div[2]/div[1]/div/div[1]";
|
||||
String CALCULATION_NAME = FIRST_APPLICATION_DIV + "/div[2]/div[contains(text(), 'TestDuplikace')]";
|
||||
String FIRST_APPLICATION_DIV = "//h2/span[contains(text(), 'Dnes')]/../../div[1]";
|
||||
String CALCULATION_NAME = FIRST_APPLICATION_DIV + "/div/div/div/div[contains(text(), 'TestDuplikace')]";
|
||||
String CALCULATION_STATUS = FIRST_APPLICATION_DIV + "//span[contains(text(), 'Rozpracovaná žádost')]";
|
||||
String CALCULATION_DATE = FIRST_APPLICATION_DIV + "/div[3]/div[2]";
|
||||
String CALCULATION_DATE = FIRST_APPLICATION_DIV + "/div/div/div[3]/div[2]";
|
||||
String DATE_FROM = "//label[contains(., 'Datum od')]/following-sibling::div/button";
|
||||
String DATE_TO = "//label[contains(., 'Datum do')]/following-sibling::div/button";
|
||||
String SEARCH_BUTTON = "//button[@testid='bbutton' and span[text()='Zobrazit']]";
|
||||
|
||||
@ -145,6 +145,7 @@ public interface VehiclePage extends SmartAutoFlow<VehiclePage>, StoreAccessor {
|
||||
@Click(BUTTON_SEARCH)
|
||||
VehiclePage clickSearchWithVin();
|
||||
|
||||
@Wait(value = BUTTON_TO_QUEUE, until = Until.CLICKABLE)
|
||||
@Click(value = BUTTON_TO_QUEUE, jsClick = true)
|
||||
VehiclePage clickToQueue();
|
||||
|
||||
|
||||
@ -9,9 +9,9 @@ public class CalculationData {
|
||||
private Brand brand;
|
||||
private Model model;
|
||||
private FilterFuel fuel;
|
||||
private String volume;
|
||||
private String power;
|
||||
private String weight;
|
||||
private int volume;
|
||||
private int power;
|
||||
private int weight;
|
||||
private int price;
|
||||
private String vin;
|
||||
private Vat vat;
|
||||
@ -25,7 +25,7 @@ public class CalculationData {
|
||||
private Subsidy subsidy;
|
||||
private ItemType itemType;
|
||||
private int firstPayment;
|
||||
private String numberOfSeats;
|
||||
private int numberOfSeats;
|
||||
|
||||
public CalculationData setCustomer(Customer customer) {
|
||||
this.customer = customer;
|
||||
@ -117,22 +117,22 @@ public class CalculationData {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CalculationData setVolume(String volume) {
|
||||
public CalculationData setVolume(int volume) {
|
||||
this.volume = volume;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CalculationData setPower(String power) {
|
||||
public CalculationData setPower(int power) {
|
||||
this.power = power;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CalculationData setWeight(String weight) {
|
||||
public CalculationData setWeight(int weight) {
|
||||
this.weight = weight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CalculationData setNumberOfSeats(String numberOfSeats) {
|
||||
public CalculationData setNumberOfSeats(int numberOfSeats) {
|
||||
this.numberOfSeats = numberOfSeats;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@ package cz.moneta.test.dsl.cagw.api;
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.cagw.CaGwBuilder;
|
||||
import cz.moneta.test.dsl.cagw.api.cbl.CblBuilder;
|
||||
import cz.moneta.test.dsl.cagw.api.v1.V1Builder;
|
||||
import cz.moneta.test.dsl.cagw.api.v2.V2Builder;
|
||||
import cz.moneta.test.dsl.cagw.api.v4.V4Builder;
|
||||
import cz.moneta.test.dsl.cagw.api.v1.V1Builder;
|
||||
|
||||
public class ApiBuilder extends CaGwBuilder {
|
||||
|
||||
@ -17,6 +17,10 @@ public class ApiBuilder extends CaGwBuilder {
|
||||
return new CblBuilder(harness);
|
||||
}
|
||||
|
||||
public V1Builder v1() {
|
||||
return new V1Builder(harness);
|
||||
}
|
||||
|
||||
public V2Builder v2() {
|
||||
return new V2Builder(harness);
|
||||
}
|
||||
@ -25,8 +29,4 @@ public class ApiBuilder extends CaGwBuilder {
|
||||
return new V4Builder(harness);
|
||||
}
|
||||
|
||||
public V1Builder v1() {
|
||||
return new V1Builder(harness);
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,6 +17,8 @@ import static cz.moneta.test.dsl.exevido.pages.inheritance.FoldersPage.PAGE_LABE
|
||||
public interface LeftMenu<T> {
|
||||
|
||||
String PAGE_SA_SMART_MENU = "sasmartmenu";
|
||||
String CATEGORY_MY_MESSAGES = "my_link";
|
||||
String SUBCATEGORY_FOR_CHECK = "myForCheck";
|
||||
String CATEGORY_INCOMING_MESSAGES = "incoming_messages_link";
|
||||
String SUBCATEGORY_ALL_INCOMING_MESSAGES_A = "all_incoming_messages";
|
||||
String CATEGORY_OUTGOING_MESSAGES = "outgoing_messages_link";
|
||||
@ -28,6 +30,12 @@ public interface LeftMenu<T> {
|
||||
String FOR_PROCESSING_D_REQUEST_MMB_XPATH = CATEGORY_INHERITANCE_MMB_XPATH + "//a[contains(., 'D - Žádost [M] - MMB')]";
|
||||
String REFRESH_COUNTERS_BUTTON = "refresh_counters";
|
||||
|
||||
@Click(value = CATEGORY_MY_MESSAGES, andWait = @Wait(value = LOADER_DIV, until = Until.GONE))
|
||||
IncomingMessagesPage clickMyMessages();
|
||||
|
||||
@Click(value = SUBCATEGORY_FOR_CHECK, andWait = @Wait(value = LOADER_DIV, until = Until.GONE))
|
||||
OutgoingMessagesPage clickForCheckMessages();
|
||||
|
||||
@Click(value = CATEGORY_INCOMING_MESSAGES, andWait = @Wait(value = LOADER_DIV, until = Until.GONE))
|
||||
IncomingMessagesPage clickIncomingMessages();
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ public interface DetailIncomingMessagePage extends ExevidoWebFlow<DetailIncoming
|
||||
DetailIncomingMessagePage saveFolderIssueNumber();
|
||||
|
||||
@Click(value = REMOVE_FOLDER_ISSUE_NUMBER, by = Lookup.XPATH, isStringDynamicXpath = true)
|
||||
@Click(CONFIRM_BUTTON)
|
||||
@Click(value = CONFIRM_BUTTON, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
DetailIncomingMessagePage removeFolderIssueNumber(String folderIssueNumber);
|
||||
|
||||
@CheckElementContent(value = MESSAGE_ID_SPAN)
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package cz.moneta.test.dsl.exevido.pages;
|
||||
|
||||
import cz.moneta.test.dsl.exevido.ExevidoWebFlow;
|
||||
import cz.moneta.test.dsl.exevido.components.ExevidoPanels;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.exevido.pages.DetailIncomingMessagePage.PAGE_LABEL;
|
||||
|
||||
@Wait(value = PAGE_LABEL)
|
||||
public interface DetailOutgoingMessagePage extends ExevidoWebFlow<DetailOutgoingMessagePage> {
|
||||
|
||||
String PAGE_LABEL = "outgoing_message_label";
|
||||
String SEND_REPLY_BUTTON_XPATH = "//button[@title='Vytvoří ke zprávě odpověď a odešle']";
|
||||
String RETURN_TO_PROCESSING_BUTTON_XPATH = "//button[@title='Vrátí zprávu ke zpracování']";
|
||||
String FOR_CHECK_REASON_INPUT = "forCheckReason";
|
||||
String ADD_FOR_REASON_BUTTON = "addForReasonButton";
|
||||
|
||||
@Click(value = SEND_REPLY_BUTTON_XPATH, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
IncomingMessagesPage clickSendReply();
|
||||
|
||||
@Click(value = RETURN_TO_PROCESSING_BUTTON_XPATH, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
DetailOutgoingMessagePage clickReturnToProcessing();
|
||||
|
||||
@TypeInto(FOR_CHECK_REASON_INPUT)
|
||||
DetailOutgoingMessagePage fillReason(String reason);
|
||||
|
||||
@Click(value = ADD_FOR_REASON_BUTTON, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
IncomingMessagesPage clickAddReason();
|
||||
|
||||
}
|
||||
@ -43,7 +43,6 @@ public interface IncomingMessagesPage extends ExevidoWebFlow<IncomingMessagesPag
|
||||
@CustomAction
|
||||
default DetailIncomingMessagePage clickIncomingMessageRow(int timeout) {
|
||||
ExevidoEndpoint endpoint = getEndpoint(ExevidoEndpoint.class);
|
||||
endpoint.sendKeysOneAtATime(Key.ENTER);
|
||||
endpoint.waitForElementAndRefresh(INCOMING_MESSAGE_ROW, Lookup.ID, endpoint::refeshPage, timeout, 2);
|
||||
endpoint.click(() -> INCOMING_MESSAGE_ROW, Lookup.ID);
|
||||
return null;
|
||||
|
||||
@ -12,8 +12,9 @@ import static cz.moneta.test.dsl.exevido.pages.NewIncomingMessagePage.PAGE_LABEL
|
||||
@Wait(PAGE_LABEL_B)
|
||||
public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessagePage>, StoreAccessor {
|
||||
|
||||
String MESSAGE_ID_KEY = "MESSAGE_ID";
|
||||
String INCOMING_MESSAGE_ID_KEY = "INCOMING_MESSAGE_ID";
|
||||
String PAGE_LABEL_B = "outgoing_message_label";
|
||||
String ANSWER_BUTTON = "answer";
|
||||
String SUBJECT_INPUT = "subject";
|
||||
String SENDER_XPATH = "//iq-select2[@id='_selectId']//input";
|
||||
String FILE_UPLOAD_XPATH = "(//input[@type='file' and @multiple='multiple'])";
|
||||
@ -28,6 +29,8 @@ public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessag
|
||||
String ITEM_TYPE_OF_MESSAGE = "//*[starts-with(@id,'item_') and @title= '%s']";
|
||||
String SENDER_ITEM = "item_0";
|
||||
String DELEGATE_BUTTON = "delegate_button_id";
|
||||
String CREATE_COPY_BUTTON = "create_copy";
|
||||
String CONFIRM_ATTACHMENT_BUTTON = "//app-actis-button//button[normalize-space()='Potvrdit']";
|
||||
String DROPDOWN_CREATE_DUPLICATE_BUTTON = "//app-actis-button[@buttonid='create_copy']/following-sibling::button";
|
||||
String SEND_TO_ANOTHER_DATA_BOX_BUTTON = "//app-actis-button//button[contains(., 'Do jiné datové schránky')]";
|
||||
String UNIVERSAL_SINGLE_SELECT = "singleSelect";
|
||||
@ -35,6 +38,13 @@ public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessag
|
||||
String UNIVERSAL_SUBMIT_SINGLE_SELECT_BUTTON = "submitSingleSelect";
|
||||
String CONFIRM_BUTTON = "//button[normalize-space()='Potvrdit']";
|
||||
String CHANGE_MESSAGE_TYPE = "change_message_type";
|
||||
String FIELD_VALIDATION_LABEL = "//b[text()='Kontrola polí']";
|
||||
String FOLDER_FILE_NUMBER_XPATH = "//iq-select2[@id='folderFileNumber']//input";
|
||||
String FOLDER_FILE_NUMBER_ITEM_0 = "(//iq-select2[@id='folderFileNumber']//div[@id='item_0'])";
|
||||
String SAVE_CHECKED_FIELD = "save_checked_field";
|
||||
|
||||
@Click(value = ANSWER_BUTTON, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewOutgoingMessagePage clickAnswer();
|
||||
|
||||
@TypeInto(SUBJECT_INPUT)
|
||||
NewIncomingMessagePage fillSubject(String subject);
|
||||
@ -83,7 +93,7 @@ public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessag
|
||||
default NewIncomingMessagePage storeMessageId() {
|
||||
ExevidoEndpoint endpoint = getEndpoint(ExevidoEndpoint.class);
|
||||
String messageId = endpoint.getText(MESSAGE_ID_SPAN).replaceAll("ID:(\\d+)", "$1");
|
||||
store(MESSAGE_ID_KEY, messageId);
|
||||
store(INCOMING_MESSAGE_ID_KEY, messageId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -96,6 +106,12 @@ public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessag
|
||||
return null;
|
||||
}
|
||||
|
||||
@Click(CREATE_COPY_BUTTON)
|
||||
NewIncomingMessagePage clickCreateCopy();
|
||||
|
||||
@Click(value = CONFIRM_ATTACHMENT_BUTTON, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewIncomingMessagePage clickConfirmAttachment();
|
||||
|
||||
@Click(value = DROPDOWN_CREATE_DUPLICATE_BUTTON, by = Lookup.XPATH)
|
||||
@Click(value = SEND_TO_ANOTHER_DATA_BOX_BUTTON, by = Lookup.XPATH)
|
||||
NewIncomingMessagePage sendToAnotherDataBox();
|
||||
@ -114,4 +130,18 @@ public interface NewIncomingMessagePage extends ExevidoWebFlow<NewIncomingMessag
|
||||
|
||||
@Click(CHANGE_MESSAGE_TYPE)
|
||||
NewIncomingMessagePage clickChangeMessageType();
|
||||
|
||||
@CustomAction
|
||||
default NewIncomingMessagePage clickFolderFileNumber() {
|
||||
ExevidoEndpoint endpoint = getEndpoint(ExevidoEndpoint.class);
|
||||
endpoint.scrollIntoView(FIELD_VALIDATION_LABEL, Lookup.XPATH);
|
||||
endpoint.click(() -> FOLDER_FILE_NUMBER_XPATH, Lookup.XPATH);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Click(value = FOLDER_FILE_NUMBER_ITEM_0, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewIncomingMessagePage clickFirstFolderFile();
|
||||
|
||||
@Click(value = SAVE_CHECKED_FIELD, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewIncomingMessagePage clickSaveCheckedField();
|
||||
}
|
||||
@ -13,9 +13,13 @@ import static cz.moneta.test.dsl.exevido.pages.NewOutgoingMessagePage.PAGE_LABEL
|
||||
@Wait(PAGE_LABEL_B)
|
||||
public interface NewOutgoingMessagePage extends ExevidoWebFlow<NewOutgoingMessagePage>, StoreAccessor {
|
||||
|
||||
String MESSAGE_ID_KEY = "MESSAGE_ID";
|
||||
String OUTGOING_MESSAGE_ID_KEY = "OUTGOING_MESSAGE_ID";
|
||||
String PDF_VIEWER_CONTENT_KEY = "PDF_VIEWER_CONTENT";
|
||||
String PAGE_LABEL_B = "outgoing_message_label";
|
||||
String INSPECTION_BUTTON = "//button[normalize-space()='Kontrola']";
|
||||
String SELECT_REVIEWER_INPUT = "//iq-select2[@id='singleSelect']//input[@type='text']";
|
||||
String SELECT_REVIEWER = "//div[contains(@class,'select2-dropdown-item') and contains(@title, '%s')]";
|
||||
String SUBMIT_SINGLE_SELECT = "submitSingleSelect";
|
||||
String SAVE_CONCEPT_BUTTON = "save_concept";
|
||||
String RECIPIENT_XPATH = "//iq-select2[@id='recipient_id']//input";
|
||||
String MULTIPLE_RECIPIENT_XPATH = "//input[@placeholder='Vyberte id příjemce']";
|
||||
@ -31,6 +35,7 @@ public interface NewOutgoingMessagePage extends ExevidoWebFlow<NewOutgoingMessag
|
||||
String TEMPLATE_FOOTER = "document-footer";
|
||||
String PDF_TEMPLATE_GENERATING_BUTTON = "IncomingMessagesActionButton";
|
||||
String EMPTY_TEMPLATE = "//ul[@id='pdf_templates_firstLevel']/li//li/a[text()='Prazdná šablona' or text()='Prázdná šablona MMB']";
|
||||
String APPLICATION_TEMPLATE = "//ul[@id='pdf_templates_secondLevel']//a[text()='Přihláška']";
|
||||
String EDIT_TEMPLATE_BUTTON = "edit_attachment";
|
||||
String SAVE_TEMPLATE_XPATH = "//tbody[@id='attachment_table_body']//i[@title='Uložit']/..";
|
||||
String UNPACK_MESSAGE_INFORMATIONS = "unpack_message_informations";
|
||||
@ -50,7 +55,20 @@ public interface NewOutgoingMessagePage extends ExevidoWebFlow<NewOutgoingMessag
|
||||
String BACK_BUTTON = "back_button";
|
||||
String PDF_VIEWER = "viewer";
|
||||
|
||||
@Click(value = INSPECTION_BUTTON, by = Lookup.XPATH)
|
||||
NewOutgoingMessagePage clickInspection();
|
||||
|
||||
@TypeInto(value = SELECT_REVIEWER_INPUT, by = Lookup.XPATH)
|
||||
NewOutgoingMessagePage fillReviewer(String text);
|
||||
|
||||
@Click(value = SELECT_REVIEWER, by = Lookup.XPATH, isStringDynamicXpath = true, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewOutgoingMessagePage clickReviewer(String reviewer);
|
||||
|
||||
@Click(value = SUBMIT_SINGLE_SELECT, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewOutgoingMessagePage clickSubmitSingleSelect();
|
||||
|
||||
@TypeInto(value = RECIPIENT_XPATH, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
@KeyPress(value = Key.ENTER)
|
||||
NewOutgoingMessagePage fillRecipient(String recipient);
|
||||
|
||||
@TypeInto(value = MULTIPLE_RECIPIENT_XPATH, by = Lookup.XPATH, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
@ -93,6 +111,10 @@ public interface NewOutgoingMessagePage extends ExevidoWebFlow<NewOutgoingMessag
|
||||
andWait = @Wait(value = TEMPLATE_CREATING_ANNOUNCEMENT_XPATH, by = Lookup.XPATH, until = Until.GONE))
|
||||
NewOutgoingMessagePage clickEmptyTemplateMmb();
|
||||
|
||||
@Click(value = APPLICATION_TEMPLATE, by = Lookup.XPATH, jsClick = true, andWait = @Wait(value = TEMPLATE_CREATING_ANNOUNCEMENT_XPATH, by = Lookup.XPATH, until = Until.GONE))
|
||||
@Wait(value = GENERATE_PDF, by = Lookup.XPATH, until = Until.GONE)
|
||||
NewOutgoingMessagePage clickApplication();
|
||||
|
||||
@Click(value = EDIT_TEMPLATE_BUTTON, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
NewOutgoingMessagePage clickEditTemplate();
|
||||
|
||||
@ -179,7 +201,7 @@ public interface NewOutgoingMessagePage extends ExevidoWebFlow<NewOutgoingMessag
|
||||
|
||||
ExevidoEndpoint endpoint = getEndpoint(ExevidoEndpoint.class);
|
||||
String messageId = endpoint.getText(MESSAGE_ID_SPAN).replaceAll("ID:(\\d+)", "$1");
|
||||
store(MESSAGE_ID_KEY, messageId);
|
||||
store(OUTGOING_MESSAGE_ID_KEY, messageId);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -1,19 +1,34 @@
|
||||
package cz.moneta.test.dsl.exevido.pages;
|
||||
|
||||
import cz.moneta.test.dsl.exevido.ExevidoWebFlow;
|
||||
import cz.moneta.test.harness.support.web.Click;
|
||||
import cz.moneta.test.harness.support.web.Lookup;
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.dsl.exevido.components.ExevidoPanels;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.exevido.ExevidoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.exevido.pages.OutgoingMessagesPage.PAGE_LABEL_XPATH;
|
||||
|
||||
@Wait(value = PAGE_LABEL_XPATH, by = Lookup.XPATH)
|
||||
public interface OutgoingMessagesPage extends ExevidoWebFlow<OutgoingMessagesPage> {
|
||||
public interface OutgoingMessagesPage extends ExevidoWebFlow<OutgoingMessagesPage>, StoreAccessor {
|
||||
|
||||
String PAGE_LABEL_XPATH = "//b[contains(text(), 'Odchozí zprávy dle stavu')]";
|
||||
String CREATE_NEW_OUTGOING_MESSAGE_BUTTON = "create_new_outgoing_message";
|
||||
String ID_MESSAGE_INPUT = "text_message_id";
|
||||
String OUTGOING_MESSAGE_ROW = "outgoing_message_0_row";
|
||||
|
||||
@Click(CREATE_NEW_OUTGOING_MESSAGE_BUTTON)
|
||||
NewOutgoingMessagePage clickCreateNewOutgoingMessage();
|
||||
|
||||
@TypeInto(value = ID_MESSAGE_INPUT, clear = true)
|
||||
@KeyPress(value = Key.ENTER, andWait = @Wait(value = ExevidoPanels.LOADER_DIV, until = Until.GONE))
|
||||
OutgoingMessagesPage searchByMessageId(String id);
|
||||
|
||||
@CustomAction
|
||||
default DetailOutgoingMessagePage clickOutgoingMessageRow(int timeout) {
|
||||
ExevidoEndpoint endpoint = getEndpoint(ExevidoEndpoint.class);
|
||||
endpoint.waitForElementAndRefresh(OUTGOING_MESSAGE_ROW, Lookup.ID, endpoint::refeshPage, timeout, 2);
|
||||
endpoint.click(() -> OUTGOING_MESSAGE_ROW, Lookup.ID);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package cz.moneta.test.dsl.exevido.tasks;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.exevido.pages.IncomingMessagesPage;
|
||||
import cz.moneta.test.dsl.exevido.pages.NewIncomingMessagePage;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CreateNewIncomingMessageTasks {
|
||||
|
||||
private final Harness harness;
|
||||
|
||||
public CreateNewIncomingMessageTasks(Harness harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public Function<IncomingMessagesPage, NewIncomingMessagePage> forInheritance(String subject, String sender, String attachment) {
|
||||
return start -> start
|
||||
.clickCreateNewIncomingMessage()
|
||||
.fillSubject(subject)
|
||||
.fillSender(sender)
|
||||
.fillTypeMessage("D - Žádost [M] - MMB")
|
||||
.uploadAttachment(attachment)
|
||||
.clickSave()
|
||||
.acceptAlertIfVisible()
|
||||
.storeMessageId();
|
||||
}
|
||||
}
|
||||
@ -17,4 +17,16 @@ public class ExevidoTasks {
|
||||
public SelectDataBoxTasks selectDataBox() {
|
||||
return new SelectDataBoxTasks(harness);
|
||||
}
|
||||
|
||||
public CreateNewIncomingMessageTasks createNewIncomingMessageTasks() {
|
||||
return new CreateNewIncomingMessageTasks(harness);
|
||||
}
|
||||
|
||||
public IncomingMessageTasks incomingMessageTasks() {
|
||||
return new IncomingMessageTasks(harness);
|
||||
}
|
||||
|
||||
public OutgoingMessageTasks outgoingMessageTasks() {
|
||||
return new OutgoingMessageTasks(harness);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package cz.moneta.test.dsl.exevido.tasks;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.exevido.pages.NewIncomingMessagePage;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class IncomingMessageTasks {
|
||||
|
||||
private final Harness harness;
|
||||
|
||||
public IncomingMessageTasks(Harness harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public Function<NewIncomingMessagePage, NewIncomingMessagePage> selectFirstFolderFileAndSave() {
|
||||
return start -> start
|
||||
.clickFolderFileNumber()
|
||||
.clickFirstFolderFile()
|
||||
.clickSaveCheckedField();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package cz.moneta.test.dsl.exevido.tasks;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.exevido.pages.NewOutgoingMessagePage;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class OutgoingMessageTasks {
|
||||
|
||||
private final Harness harness;
|
||||
|
||||
public OutgoingMessageTasks(Harness harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public Function<NewOutgoingMessagePage, NewOutgoingMessagePage> assignReviewerAndSubmit(String reviewer) {
|
||||
return start -> start
|
||||
.fillReviewer(reviewer)
|
||||
.clickReviewer(reviewer)
|
||||
.clickSubmitSingleSelect();
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,10 @@ package cz.moneta.test.dsl.imq;
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionEndpoint;
|
||||
import cz.moneta.test.harness.endpoints.imq.ImqFirstVisionQueue;
|
||||
import cz.moneta.test.harness.messaging.ReceivedMessage;
|
||||
import cz.moneta.test.harness.messaging.exception.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.messaging.ReceivedMessage;
|
||||
import cz.moneta.test.harness.support.messaging.exception.MessagingTimeoutException;
|
||||
import cz.moneta.test.harness.support.messaging.ImqRequest;
|
||||
import cz.moneta.test.harness.messaging.MqMessageFormat;
|
||||
import cz.moneta.test.harness.support.messaging.MqMessageFormat;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
package cz.moneta.test.dsl.monetaapiportal;
|
||||
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
import static cz.moneta.test.dsl.monetaapiportal.LoginPage.PAGE_LOGO;
|
||||
|
||||
@Wait(value = PAGE_LOGO)
|
||||
public interface LoginPage extends WebFlow<LoginPage> {
|
||||
|
||||
String PAGE_LOGO = "provider-name";
|
||||
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package cz.moneta.test.dsl.monetaapiportal;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.monetaapiportal.pages.LoginPage;
|
||||
import cz.moneta.test.harness.endpoints.monetaapiportal.MonetaApiPortalEndpoint;
|
||||
import cz.moneta.test.harness.support.web.Builders;
|
||||
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package cz.moneta.test.dsl.monetaapiportal.pages;
|
||||
|
||||
import cz.moneta.test.harness.support.web.Lookup;
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
import static cz.moneta.test.dsl.monetaapiportal.pages.LoginPage.MONETA_LOGO;
|
||||
|
||||
@Wait(value = MONETA_LOGO, waitSecondsForElement = 60, by = Lookup.XPATH)
|
||||
public interface LoginPage extends WebFlow<LoginPage> {
|
||||
|
||||
String MONETA_LOGO = "//div[@class='header-nav-brand']";
|
||||
|
||||
}
|
||||
@ -36,8 +36,8 @@ public interface DashboardPage extends NewIbPageFlow<DashboardPage>, HorizontalM
|
||||
String BANNER_SU_EUR = "//span[starts-with(text(), 'EUR spořicí účet s úrokem')]";
|
||||
String CARDS_BTN_XPATH = "//li[@data-testid='cards-link']";
|
||||
String RTV_CARD = "//small[contains(., '%s')]";
|
||||
String PENSION_CARD_XPATH = "//div[contains(@class,'productCard__header')][.//span[contains(., 'Penze')] and .//small[contains(., 'Doplňkové penzijní spoření')]]";
|
||||
String PENSION_CARD_SAVED_TEXT_XPATH = "//div[contains(@class,'productCard__ballance__ledger')]//small[normalize-space(.)='Naspořeno']";
|
||||
String PENSION_CARD_XPATH = "//small[contains(., 'Doplňkové penzijní spoření')]";
|
||||
String PENSION_CARD_SAVED_TEXT_XPATH = "//*[@data-testid='TextComponent']//small[normalize-space(.)='Naspořeno']";
|
||||
String USE_MOBILE_KEY_BUTTON_XPATH = "//button[@data-testid='buttonUseMobileKey']";
|
||||
String CHECK_BOX_MOBILE_KEY_XPATH = "//span[@class='f-checkbox__indicator']";
|
||||
String CONFIRMATION_MOBILE_KEY_XPATH = "//button[@data-testid='confirmButton']";
|
||||
|
||||
@ -76,6 +76,7 @@ public interface SavingsPage extends NewIbPageFlow<SavingsPage>, StoreAccessor,
|
||||
@CheckElementPresent(RTV_DEPOSIT_BLUE_ALERT_TEXT_XPATH)
|
||||
SavingsPage checkRtvDepositBlueAlertText();
|
||||
|
||||
@Wait(explicitWaitSeconds = 2)//wait z dôvodu, že pri rýchlom storne práve založenej TRN dochádza k častým pádom
|
||||
@Click(value = RTV_SERVICING_STORNO_BUTTON)
|
||||
RtvServicingPage clickOnStornoButton();
|
||||
|
||||
|
||||
@ -144,6 +144,7 @@ public interface RtvServicingPage extends NewIbPageFlow<RtvServicingPage> {
|
||||
@Click(RTV_SERVICING_CANCEL_CONTINUE_BUTTON_XPATH)
|
||||
RtvServicingPage clickOnContinueRtvServicingCancelButton();
|
||||
|
||||
@Wait(explicitWaitSeconds = 4)//wait z dôvodu, že pri rýchlom storne práve založenej TRN dochádza k častým pádom
|
||||
@Click(RTV_SERVICING_STORNO_BUTTON_ON_VICTORY)
|
||||
RtvServicingPage clickOnRtvServicingStornoButtonOnVictory();
|
||||
}
|
||||
@ -11,7 +11,7 @@ public interface CardsSettingsPage extends NewIbPageFlow<CardsSettingsPage> {
|
||||
String CREDIT_CARDS_TAB_XPATH = "//span[@data-testid='credit-cards-tab' and text()='Kreditní']";
|
||||
String NO_CREDIT_CARDS_XPATH = "//div[@data-testid='TextComponent' and text()='Momentálně nemáte žádné kreditní karty.']";
|
||||
String DEBIT_CARDS_TAB_XPATH = "//span[@data-testid='debit-cards-tab' and text()='Debetní']";
|
||||
String DEBIT_CARDS_TO_THE_ACCOUNT_XPATH = "//span[@data-testid='TextComponent']/small[normalize-space(.)='Debetní karty k účtu']";
|
||||
String DEBIT_CARDS_TO_THE_ACCOUNT_XPATH = "//td[h2//small[normalize-space()='Debetní karty k účtu']]";
|
||||
String CARDS_TO_THE_ACCOUNT_XPATH = "//h5[@class='t-title t-title--h5 u-mb--0 u-mr--0' and text()='Karty k účtu (2)']";
|
||||
String CARD_DETAIL_BUTTON_XPATH = "//div[@class='c-card u-p--xSmall u-pb--0 item-row']";
|
||||
String CARD_DETAIL_TITLE_XPATH = "//h3[@class='t-title t-title--h5 u-pl--large' and text()='Informace o kartě, nastavení']";
|
||||
@ -63,7 +63,7 @@ public interface CardsSettingsPage extends NewIbPageFlow<CardsSettingsPage> {
|
||||
String CARD_BLOCKING_VICTORY_TITLE_XPATH = "//h2[@class='t-title t-title--h4 styles_title__NAx0Q' and contains(., 'Kartu udržíme pod zámkem do')]";
|
||||
String CARD_BLOCKING_VICTORY_TEXT_XPATH = "//p[@data-testid='TextComponent' and contains(normalize-space(.), 'Po tomto datu ji opět odblokujeme a vás informujeme SMS na číslo +420 702 111 111.') and contains(normalize-space(.), 'Můžete ji do té doby kdykoli odblokovat nebo trvale zablokovat.')]";
|
||||
String BACK_BUTTON_ON_VICTORY_SCREEN_XPATH = "//button[@data-testid='card-block-victoryNext' and text()='Zpět na kartu']";
|
||||
String UNBLOCK_BUTTON_XPATH = "//button[@class='c-btn c-btn--small c-btn--border c-btn--primary c-compact-buttons__button' and text()='Odblokovat kartu']";
|
||||
String UNBLOCK_BUTTON_XPATH = "//button[@type='button' and text()='Odblokovat kartu']";
|
||||
String CARD_BLOCKING_TYPES_ON_UNBLOCK_SCREEN_XPATH = "//span[@class='f-radio__text' and contains(., 'Odblokovat') or contains(., 'Zablokovat a vydat novou') or contains(., 'Zablokovat a zrušit')]";
|
||||
String CARD_UNBLOCKING_VICTORY_TITLE_XPATH = "//h2[@class='t-title t-title--h4 styles_title__NAx0Q' and contains(., 'Karta je znovu aktivní')]";
|
||||
String CARD_UNBLOCKING_VICTORY_TEXT_XPATH = "//p[@data-testid='TextComponent' and contains(., 'Můžete s ní začít okamžitě platit.')]";
|
||||
@ -76,6 +76,8 @@ public interface CardsSettingsPage extends NewIbPageFlow<CardsSettingsPage> {
|
||||
String CREDIT_LIMIT_INCREASE_MODAL_XPATH = "//h2[@class='t-title t-title--h3 c-modal__title' and text()='Na této funkci pracujeme']";
|
||||
String CREDIT_LIMIT_INCREASE_CLOSE_MODAL_BUTTON_XPATH = "//button[@class='c-btn c-btn--border c-btn--primary' and text()='Zavřít']";
|
||||
String CREDIT_CARD_XPATH = "//small[normalize-space(.)='MoneyCard Smart']";
|
||||
String CANCEL_ACCOUNT = "//button[@data-testid='account-close' and text()='Zrušit tento běžný účet']";
|
||||
String ACCOUNT_NUMBER = "//th[span[@data-testid='TextComponent' and text()='Číslo účtu']]";
|
||||
|
||||
@Click(CREDIT_CARDS_TAB_XPATH)
|
||||
CardsSettingsPage clickOnCreditCardsTab();
|
||||
@ -83,14 +85,14 @@ public interface CardsSettingsPage extends NewIbPageFlow<CardsSettingsPage> {
|
||||
@CheckElementPresent(NO_CREDIT_CARDS_XPATH)
|
||||
CardsSettingsPage checkNoCreditCards();
|
||||
|
||||
@Click(DEBIT_CARDS_TAB_XPATH)
|
||||
@Click(value = DEBIT_CARDS_TAB_XPATH, andWait = @Wait(explicitWaitSeconds = 1))
|
||||
CardsSettingsPage clickOnDebitCardsTab();
|
||||
|
||||
@Wait(value = DEBIT_CARDS_TO_THE_ACCOUNT_XPATH, until = Until.CLICKABLE)
|
||||
@Click(DEBIT_CARDS_TO_THE_ACCOUNT_XPATH)
|
||||
@Click(value = DEBIT_CARDS_TO_THE_ACCOUNT_XPATH, andWait = @Wait(explicitWaitSeconds = 1))
|
||||
CardsSettingsPage clickOnDebitCardsToTheAccount();
|
||||
|
||||
@Wait(value = CARDS_TO_THE_ACCOUNT_XPATH, until = Until.CLICKABLE)
|
||||
@Wait(value = CANCEL_ACCOUNT, until = Until.VISIBLE)
|
||||
@Click(CARDS_TO_THE_ACCOUNT_XPATH)
|
||||
CardsSettingsPage clickOnCardsToTheAccount();
|
||||
|
||||
@ -98,7 +100,7 @@ public interface CardsSettingsPage extends NewIbPageFlow<CardsSettingsPage> {
|
||||
@Click(CARD_DETAIL_BUTTON_XPATH)
|
||||
CardsSettingsPage clickOnCardDetail();
|
||||
|
||||
@Wait(value = CARD_DETAIL_TITLE_XPATH, until = Until.CLICKABLE)
|
||||
@Wait(ACCOUNT_NUMBER)
|
||||
@CheckElementPresent(CARD_DETAIL_TITLE_XPATH)
|
||||
CardsSettingsPage checkCardDetailTitle();
|
||||
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
package cz.moneta.test.dsl.oauth2.web;
|
||||
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.oauth2.web.EchoPage.ECHO_TEXT;
|
||||
|
||||
@Wait(value = ECHO_TEXT, waitSecondsForElement = 40, by = Lookup.XPATH)
|
||||
public interface EchoPage extends WebFlow<Oauth2WebTransitions> {
|
||||
|
||||
String ECHO_TEXT = "//pre[contains(text(),'Test echo service: OK')]";
|
||||
}
|
||||
@ -4,7 +4,6 @@ import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.harness.endpoints.oauth2.web.Oauth2WebEndpoint;
|
||||
import cz.moneta.test.harness.support.web.Builders;
|
||||
|
||||
@Deprecated() // Needs completely review - If you want to work with this, please contact Harness admin firstly.
|
||||
public class Oauth2Web {
|
||||
|
||||
private final Harness harness;
|
||||
@ -13,21 +12,18 @@ public class Oauth2Web {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
@Deprecated() // Will be reviewed in the API Lab team.
|
||||
public Oauth2WebTransitions onOauth2WebTransitions() {
|
||||
harness.getEndpoint(Oauth2WebEndpoint.class).openApplication();
|
||||
return Builders.newWebFlowBuilder(Oauth2WebTransitions.class, harness.getEndpoint(Oauth2WebEndpoint.class), harness);
|
||||
}
|
||||
|
||||
@Deprecated() // Will be reviewed in the API Lab team.
|
||||
public Oauth2WebTransitionsKyc onOauth2WebTransitionsKyc() {
|
||||
harness.getEndpoint(Oauth2WebEndpoint.class).openApplication();
|
||||
return Builders.newWebFlowBuilder(Oauth2WebTransitionsKyc.class, harness.getEndpoint(Oauth2WebEndpoint.class), harness);
|
||||
}
|
||||
|
||||
public Oauth2WebEcho onOauth2WebEcho() {
|
||||
harness.getEndpoint(Oauth2WebEndpoint.class).openApplication();
|
||||
return Builders.newWebFlowBuilder(Oauth2WebEcho.class, harness.getEndpoint(Oauth2WebEndpoint.class), harness);
|
||||
}
|
||||
|
||||
public Oauth2WebFederatedLogin onOauth2WebFederatedLogin() {
|
||||
harness.getEndpoint(Oauth2WebEndpoint.class).openApplication();
|
||||
return Builders.newWebFlowBuilder(Oauth2WebFederatedLogin.class, harness.getEndpoint(Oauth2WebEndpoint.class), harness);
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
package cz.moneta.test.dsl.oauth2.web;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.oauth2.web.Oauth2WebEndpoint;
|
||||
import cz.moneta.test.harness.support.web.CustomAction;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface Oauth2WebEcho extends WebFlow<Oauth2WebEcho>, StoreAccessor {
|
||||
String ECHO_PAGE_URL = "api/echo";
|
||||
|
||||
@CustomAction
|
||||
default EchoPage callEcho() {
|
||||
Oauth2WebEndpoint endpoint = getEndpoint(Oauth2WebEndpoint.class);
|
||||
endpoint.changePath(ECHO_PAGE_URL);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,13 @@
|
||||
package cz.moneta.test.dsl.oauth2.web;
|
||||
|
||||
import cz.moneta.test.dsl.oauth2.web.pages.FederatedLoginPage;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.oauth2.web.Oauth2WebEndpoint;
|
||||
import cz.moneta.test.harness.support.web.CustomAction;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface Oauth2WebFederatedLogin extends WebFlow<Oauth2WebFederatedLogin>, StoreAccessor {
|
||||
String FEDERATED_LOGIN_PAGE_URL = "oauth2/react/#/login/?client_name=Test&state=1234&scope=ALL&return_url=https.seznam.cz";
|
||||
String FEDERATED_LOGIN_PAGE_URL = "#/login/?client_name=Test&state=1234&scope=ALL&return_url=https.seznam.cz";
|
||||
|
||||
@CustomAction
|
||||
default FederatedLoginPage callFederatedLogin() {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
package cz.moneta.test.dsl.oauth2.web;
|
||||
package cz.moneta.test.dsl.oauth2.web.pages;
|
||||
|
||||
import cz.moneta.test.dsl.oauth2.web.Oauth2WebTransitions;
|
||||
import cz.moneta.test.harness.support.web.Lookup;
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
import static cz.moneta.test.dsl.oauth2.web.FederatedLoginPage.MONETA_LOGO_IMG;
|
||||
import static cz.moneta.test.dsl.oauth2.web.pages.FederatedLoginPage.MONETA_LOGO_IMG;
|
||||
|
||||
@Wait(value = MONETA_LOGO_IMG, waitSecondsForElement = 40, by = Lookup.XPATH)
|
||||
public interface FederatedLoginPage extends WebFlow<Oauth2WebTransitions> {
|
||||
|
||||
String MONETA_LOGO_IMG = "//img[@alt='MONETA logo']";
|
||||
String MONETA_LOGO_IMG = "//img[@alt='MONETA Money Bank Logo']";
|
||||
}
|
||||
@ -35,54 +35,27 @@ public class FileForInputPreparation {
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
|
||||
while (line != null) {
|
||||
if (line.contains("#LASTFIELD#")) {
|
||||
if (lastField != null) {
|
||||
line = line.replace("#LASTFIELD#", String.valueOf(lastField + 1));
|
||||
}
|
||||
if (line.contains("#DATETIME#")) {
|
||||
line = line.replace("#DATETIME#", DateUtils.getFormattedTodayDateTime("yyyy-MM-dd'T'hh:mm:ss"));
|
||||
}
|
||||
if (line.contains("#YYYYMMDD#")) {
|
||||
line = line.replace("#YYYYMMDD#", DateUtils.getFormattedTodayDate("yyyyMMdd"));
|
||||
}
|
||||
if (line.contains("#YYMMDD#")) {
|
||||
line = line.replace("#YYMMDD#", DateUtils.getFormattedTodayDate("yyMMdd"));
|
||||
}
|
||||
if (line.contains("#YYYY-MM-DD#")) {
|
||||
line = line.replace("#YYYY-MM-DD#", DateUtils.getFormattedTodayDate("yyyy-MM-dd"));
|
||||
}
|
||||
if (line.contains("#REF#")) {
|
||||
line = line.replace("#REF#", String.valueOf(data.getTrxReference()));
|
||||
}
|
||||
if (line.contains("#IBAN#")) {
|
||||
line = line.replace("#IBAN#", String.valueOf(data.getIban()));
|
||||
}
|
||||
if (line.contains("#RANDOM#")) {
|
||||
line = line.replace("#RANDOM#", String.valueOf(data.getRandom()));
|
||||
}
|
||||
if (line.contains("#AMOUNT#")) {
|
||||
line = line.replace("#AMOUNT#", String.valueOf(data.getAmount()));
|
||||
}
|
||||
if (line.contains("#AMOUNT1#")) {
|
||||
line = line.replace("#AMOUNT1#", String.valueOf(data.getAmount1()));
|
||||
}
|
||||
if (line.contains("#CREDITOR#")) {
|
||||
line = line.replace("#CREDITOR#", String.valueOf(data.getCreditorAcc()));
|
||||
}
|
||||
if (line.contains("#DEBTOR#")) {
|
||||
line = line.replace("#DEBTOR#", String.valueOf(data.getDebtorAcc()));
|
||||
}
|
||||
if (line.contains("#BANKCODE#")) {
|
||||
line = line.replace("#BANKCODE#", String.valueOf(data.getBankCode()));
|
||||
}
|
||||
if (line.contains("#MESSAGEID#")) {
|
||||
line = line.replace("#MESSAGEID#", String.valueOf(data.getMessageId()));
|
||||
}
|
||||
if (line.contains("#FEETYPE#")) {
|
||||
line = line.replace("#FEETYPE#", String.valueOf(data.getFeeType()));
|
||||
}
|
||||
if (line.contains("#HDTYPE#")) {
|
||||
line = line.replace("#HDTYPE#", String.valueOf(data.getHdType()));
|
||||
}
|
||||
line = line.replace("#UETR#", String.valueOf(data.getUetr()));
|
||||
line = line.replace("#CREATE_DATE#", DateUtils.getFormattedTodayDateTime("yyyy-MM-dd'T'hh:mm:ss") + "+00:00");
|
||||
line = line.replace("#CREATE_DATETIME#", DateUtils.getFormattedTodayDateTime("yyyy-MM-dd'T'hh:mm:ss") + "+01:00");
|
||||
|
||||
writer.write(line);
|
||||
writer.newLine();
|
||||
|
||||
@ -13,6 +13,8 @@ import static cz.moneta.test.dsl.payment_engine.parts_of_menu.menu_search.Paymen
|
||||
|
||||
@Wait({ID, SEARCH_BUTTON})
|
||||
public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage>, StoreAccessor {
|
||||
|
||||
String ADVANCED_FILTER = "//div[label[text()='Rozšířený filtr'] and @class=' xcheckbox-wrap']/a";
|
||||
String ID = "//div[@id='Vyhledávání']//input[@name='field1#id']";
|
||||
String DEBTOR_ACC = "//div[@id='Vyhledávání']//input[@name='field1#orAccIdent']";
|
||||
String SETTLEMENT_FROM = "//input[@name='field1#settDate']//following-sibling::input";
|
||||
@ -28,6 +30,7 @@ public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage
|
||||
String REASON_IMG = REASON + "/following-sibling::img";
|
||||
String REASON_FILLED = "//input[@name='field1#queueReason' and not(@value='')]";
|
||||
String SELECTION_DIV = "//div[text()='%s']";
|
||||
String UETR_INPUT = "//input[@name='field1#uetr']";
|
||||
String SEARCH_BUTTON = "//div[@id = 'Vyhledávání']//button[text() = 'Hledat']/../..";
|
||||
String SEARCH_FORM_BUTTON = "//button[@class='x-btn-text grid-filter-collapse']";
|
||||
String CHECK_SELECTED_TRANSACTION_DATA_PATH = "//*[contains(@class,'x-grid3-row-selected')]//*[contains(text(),'%s')]";
|
||||
@ -36,7 +39,7 @@ public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage
|
||||
String TRANSACTION_FILES = "//a[text()='Soubory']";
|
||||
String TRANSACTION_LIFE_CYCLE = "//a[text()='Životní cyklus']";
|
||||
String CLOSE_TAB_BUTTON = "//a[@class='x-tab-strip-close']";
|
||||
String TRANSACTION_ID_KEY = "TRANSACTION_ID_KEY";
|
||||
String TRANSACTION_ID_KEY = "TRANSACTION_ID";
|
||||
String TRANSACTION_ID_XPATH = "//div[@class='x-panel-body x-panel-body-noheader x-border-layout-ct']//div[contains(@class,'x-grid3-row-selected')]//div[@class='x-grid3-cell-inner x-grid3-col-2']";
|
||||
String EQUIVALENT_AMOUNT_KEY = "EQUIVALENT_AMOUNT_KEY";
|
||||
String EQUIVALENT_AMOUNT_EUR_XPATH = "//div[@class='x-grid3-scroller']//div[text()='EUR']/../following-sibling::td[contains(@class, 'x-grid3-td-11')]/div";
|
||||
@ -48,6 +51,9 @@ public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage
|
||||
@TypeInto(value = ID, clear = true)
|
||||
PaymentEngineSearchPage typeId(String id);
|
||||
|
||||
@Click(ADVANCED_FILTER)
|
||||
PaymentEngineSearchPage clickAdvancedFilter();
|
||||
|
||||
@Wait(value = DEBTOR_ACC, until = Until.VISIBLE)
|
||||
@TypeInto(DEBTOR_ACC)
|
||||
PaymentEngineSearchPage typeDebtorAcc(String debtorAcc);
|
||||
@ -74,6 +80,9 @@ public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage
|
||||
@Click(value = SELECTION_DIV, isStringDynamicXpath = true, andWait = @Wait(value = REASON_FILLED, until = Until.PRESENT_IN_DOM, explicitWaitSeconds = 1))
|
||||
PaymentEngineSearchPage selectReason(String reason);
|
||||
|
||||
@TypeInto(UETR_INPUT)
|
||||
PaymentEngineSearchPage fillUetr(String uetr);
|
||||
|
||||
@Wait(value = SEARCH_BUTTON, until = Until.VISIBLE)
|
||||
@Click(value = SEARCH_BUTTON, andWait = @Wait(value = AJAX_LOADING, until = Until.GONE))
|
||||
PaymentEngineSearchPage clickSearch();
|
||||
@ -121,6 +130,6 @@ public interface PaymentEngineSearchPage extends WebFlow<PaymentEngineSearchPage
|
||||
PaymentEngineSearchPageLifeCycle clickLifeCycle();
|
||||
|
||||
@CheckElementContent(value = FRONT_LABEL, isStringDynamicXpath = true)
|
||||
PaymentEngineSearchPage checkExpectedFrontValue(String transactionId, String expectedFrontValue);
|
||||
PaymentEngineSearchPage checkExpectedFrontValueByTransactionId(String transactionId, String expectedFrontValue);
|
||||
|
||||
}
|
||||
@ -997,4 +997,14 @@ public class PaymentEngineTasks {
|
||||
harness.withPaymentEngine().closePaymentEngine();
|
||||
harness.log("Previous Authorization For Direct Debit was not found.");
|
||||
}
|
||||
|
||||
public Function<PaymentEngineHomePage, PaymentEngineSearchPage> checkTransactionStateByUetr(String uetr) {
|
||||
return start -> start
|
||||
.search()
|
||||
.clickAdvancedFilter()
|
||||
.fillUetr(uetr)
|
||||
.clickSearch()
|
||||
.storeSelectedTransactionId()
|
||||
.waitForStatus(this.harness, TransactionReasonPAE.INGOING_ARCHIV);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cz.moneta.test.dsl.payment_engine_mq;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum MessagingType {
|
||||
MT("MT"),
|
||||
MX("MX"),
|
||||
T2("T2"),
|
||||
MT101("MT101"),
|
||||
RESP("RESPONS");
|
||||
|
||||
private final String value;
|
||||
|
||||
MessagingType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package cz.moneta.test.dsl.payment_engine_mq;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.harness.constants.HarnessConfigConstants;
|
||||
import cz.moneta.test.harness.endpoints.payment_engine.PaeMqEndpoint;
|
||||
import cz.moneta.test.harness.support.auth.AuthSupport;
|
||||
import cz.moneta.test.harness.support.auth.Credentials;
|
||||
import cz.moneta.test.harness.support.rest.RawRestRequest;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class PaeMqBuilder {
|
||||
private final Harness harness;
|
||||
|
||||
public PaeMqBuilder(Harness harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public RawRestRequest.Request prepareRequest(MessagingType messagingType) {
|
||||
PaeMqEndpoint endpoint = harness.getEndpoint(PaeMqEndpoint.class);
|
||||
String environment = harness.getConfig(HarnessConfigConstants.ENVIRONMENT_TYPE);
|
||||
String qmgr = resolveQueueManager(environment);
|
||||
String path = preparePath(messagingType, qmgr, environment);
|
||||
RawRestRequest.Request request = RawRestRequest.jsonBuilder(endpoint)
|
||||
.withPath(path)
|
||||
.withHeader("Authorization", getTokenFromCredentials());
|
||||
return addDefaultHeaders(request, qmgr);
|
||||
}
|
||||
|
||||
private RawRestRequest.Request addDefaultHeaders(RawRestRequest.Request request, String qmgr) {
|
||||
return request
|
||||
.withHeader("Content-Type", "text/plain")
|
||||
.withHeader("ibm-mq-rest-csrf-token", "blank")
|
||||
.withHeader("ibm-mq-md-persistence", "persistent")
|
||||
.withHeader("ibm-mq-md-replyto", "REPLYQ")
|
||||
.withHeader("ibm-mq-md-replytoqmgr", qmgr);
|
||||
}
|
||||
|
||||
private String getTokenFromCredentials() {
|
||||
Credentials cred = AuthSupport.getCredentials("pae-mq", harness);
|
||||
return "Basic " + Base64.getEncoder().encodeToString((cred.getUsername() + ":" + cred.getPassword()).getBytes());
|
||||
}
|
||||
|
||||
private String preparePath(MessagingType messagingType, String qmgr, String environment) {
|
||||
String envPae = environment.equals("tst1") ? "TST" : "UAT";
|
||||
return String.format("qmgr/%s/queue/SAA.PAYHUB.%s.IN.%s/message", qmgr, messagingType.getValue(), envPae);
|
||||
}
|
||||
|
||||
private String resolveQueueManager(String environment) {
|
||||
return environment.equals("tst1") ? "MSWIFTT" : "MSWIFTU";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.client;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.client.ApplicationListPage.LOAD_ALL_BUTTON;
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
@Wait(LOAD_ALL_BUTTON)
|
||||
public interface ApplicationListPage extends WebFlow<ApplicationListPage>, XXX702_SharedTopMenu<ApplicationListPage> {
|
||||
String LOAD_ALL_BUTTON = "XXX702_T001_B027";
|
||||
String DETAIL_BUTTON = "XXX702_T001_P045-1";
|
||||
String LOAD_BUTTON = "XXX702_T001_B026";
|
||||
String COPY_BUTTON = "XXX702_T001_B034";
|
||||
String FINISH_BUTTON = "XXX702_T001_B029";
|
||||
String DECISION_BUTTON = "XXX702_T001_B030";
|
||||
String STORNO_BUTTON = "XXX702_T001_B028";
|
||||
String CLIENT_DETAIL = "//button [contains(@title, '[XXX702_T001_I006]')]";
|
||||
|
||||
@CheckElementContent("XXX702_T001_L303-1")
|
||||
ApplicationListPage assertFirstApplicationProductType(String firstApplicationProductType);
|
||||
|
||||
@CheckElementContent("XXX702_T001_L305-1")
|
||||
ApplicationListPage assertFirstApplicationRequestedAmount(String firstApplicationRequestedAmount);
|
||||
|
||||
@CheckElementContent("XXX702_T001_L306-1")
|
||||
ApplicationListPage assertFirstApplicationApprovedAmount(String firstApplicationApprovedAmount);
|
||||
|
||||
@Wait(explicitWaitSeconds = 1) // There is a moment when list is full of some random items, can't think of better way to track it
|
||||
@CheckElementContent("XXX702_T001_L285-1")
|
||||
ApplicationListPage assertFirstApplicationState(String firstApplicationState);
|
||||
|
||||
@CheckElementContent("XXX702_T001_L310-1")
|
||||
ApplicationListPage assertFirstApplicationChannel(String channel);
|
||||
|
||||
@CheckElementPresent(DETAIL_BUTTON)
|
||||
ApplicationListPage assertApplicationDetailButton();
|
||||
|
||||
@CheckElementPresent(LOAD_BUTTON)
|
||||
ApplicationListPage assertApplicationLoadButton();
|
||||
|
||||
@CheckElementPresent(LOAD_ALL_BUTTON)
|
||||
ApplicationListPage assertApplicationLoadAllButton();
|
||||
|
||||
@CheckElementPresent(COPY_BUTTON)
|
||||
ApplicationListPage assertApplicationCopyButton();
|
||||
|
||||
@CheckElementPresent(FINISH_BUTTON)
|
||||
ApplicationListPage assertApplicationFinishButton();
|
||||
|
||||
@CheckElementPresent(DECISION_BUTTON)
|
||||
ApplicationListPage assertApplicationDecisionButton();
|
||||
|
||||
@CheckElementPresent(STORNO_BUTTON)
|
||||
ApplicationListPage assertApplicationStornoButton();
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME)
|
||||
@Click(value = CLIENT_DETAIL, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickClientDetail();
|
||||
|
||||
}
|
||||
@ -1,21 +1,19 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.client.clientDetail;
|
||||
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_AdcProductsPanel.ADC_PRODUCT;
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
@Wait(value = ADC_PRODUCT)
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
public interface STS701_S002_AdcProductsPanel extends STS701_S002_ClientDetailPage, STS701_S002_InfoServiceExpressSettings, STS701_S002_InfoServiceOrderDetail, STS701_S002_InfoServiceOrdersList {
|
||||
public interface STS701_S002_AdcProductsPanel extends XXX702_SharedTopMenu<STS701_S002_AdcProductsPanel>, STS701_S002_InfoServiceExpressSettings, STS701_S002_InfoServiceOrderDetail, STS701_S002_InfoServiceOrdersList {
|
||||
String ADC_PRODUCT = "STS701_S002_C018";
|
||||
String CANCEL_INTERNET_BANK_BUTTON = "STS701_S002_B048";
|
||||
String CANCEL_BANK_CLIENT_BUTTON = "STS701_S002_B060";
|
||||
String FIRST_CANCEL_SMART_BANK_XPATH = "(//img[contains(@id, 'STS701_S002_P005') and not(contains(@style,'display: none;'))])[1]";
|
||||
String FIRST_SMART_BANK_CHECKBOX = FIRST_CANCEL_SMART_BANK_XPATH + "/../../..//input";
|
||||
String STATUS_MESSAGE_DIV = "XXX702_T001_L107";
|
||||
|
||||
@Click(CANCEL_INTERNET_BANK_BUTTON)
|
||||
@AcceptAlert(waitSecondsForAlert = 5)
|
||||
@ -34,13 +32,6 @@ public interface STS701_S002_AdcProductsPanel extends STS701_S002_ClientDetailPa
|
||||
@Click(value = FIRST_SMART_BANK_CHECKBOX, by = Lookup.XPATH)
|
||||
STS701_S002_AdcProductsPanel clickFirstSmartBank();
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_AdcProductsPanel checkStatusMessage(String expectedStatusMessage) {
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
endpoint.waitForElementsToLoad(5, Until.VISIBLE, STATUS_MESSAGE_DIV);
|
||||
String fullText = endpoint.getText(STATUS_MESSAGE_DIV);
|
||||
String textWithoutTime = fullText.replaceFirst("^\\d{2}:\\d{2}:\\d{2}", "");
|
||||
Assertions.assertTrue(textWithoutTime.contains(expectedStatusMessage));
|
||||
return null;
|
||||
}
|
||||
@CheckElementContent(STATUS_BAR)
|
||||
STS701_S002_AdcProductsPanel checkStatusMessage(String expectedStatusMessage);
|
||||
}
|
||||
@ -2,7 +2,6 @@ package cz.moneta.test.dsl.ufo.operations.client.clientDetail;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.banka.pages.common.pages.TIS701_S004_ConfirmationOfAccountHolding;
|
||||
import cz.moneta.test.dsl.ufo.banka.pages.main.client.settings.EVY701_S001_StatementsDistributionPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.ApplicationListPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.NewProductsByNameUfoOps;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.NewProductsUfoOps;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.STO702_S002_ApproveAlreadyReadPage;
|
||||
@ -14,34 +13,23 @@ import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage.*;
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
@Wait(LIST_OF_OFFERS)
|
||||
@Wait(value = LIST_OF_PRODUCTS, by = Lookup.XPATH)
|
||||
@Wait(value = {CIF_DIV, RC_DIV})
|
||||
@Wait(value = APPLICATIONS_BUTTON, by = Lookup.XPATH)
|
||||
@Wait(value = {CLIENT_CIF_LABEL, CLIENT_RC_LABEL})
|
||||
@Wait(value = APPLICATIONS_PANEL_XPATH, by = Lookup.XPATH)
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE, waitSecondsForElement = 80)
|
||||
public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS701_S002_ClientDetailPage>, XXX702_SharedTopMenu<STS701_S002_ClientDetailPage> {
|
||||
|
||||
String CURRENT_ACCOUNT_NUMBER_KEY = "CURRENT_ACCOUNT_NUMBER";
|
||||
String CIF_STORE_KEY = "CIF";
|
||||
String ACTUAL_ADDRESS_KEY = "ACTUAL_ADDRESS";
|
||||
String CIF_DIV = "XXX702_T002_L101";
|
||||
String RC_DIV = "XXX702_T001_L009";
|
||||
String CREATE_NEW_BUTTON = "STS701_S002_B001";
|
||||
String APPLICATIONS_BUTTON = "//button[text()='Žádosti']";
|
||||
String NEW_PRODUCT_GENERAL_XPATH = "//input[@value='%1$s']";
|
||||
String NEW_PRODUCT_BY_NAME_GENERAL_XPATH = "//table[@id='STS701_S002_G002_UfoTable']//td[text()='%1$s']";
|
||||
String LIST_OF_OFFERS = "STS701_S002_V002";
|
||||
String LIST_OF_PRODUCTS = "//fieldset[@id='STS701_S002_Y002']/legend[text()='Produkty']";
|
||||
String BACK_OFFER_MANUAL_BUTTON = "ZAS701_S002_P007";
|
||||
String LIST_CARDS_BUTTON = "STS701_S002_I021";
|
||||
String BACK_LIST_CARDS_BUTTON = "OPK730_S001_B001";
|
||||
String CHANGE_DISTRIBUTION_BUTTON = "STS701_S002_I058";
|
||||
String BACK_CHANGE_DISTRIBUTION_BUTTON = "EVY701_S001_B002";
|
||||
String ACCOUNT_LIST_XPATH = "//button[contains(@title, 'STS701_S002_I029')]";
|
||||
@ -50,22 +38,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
String ADC_PANEL_XPATH = "//button[contains(@title, 'STS701_S002_I187')]";
|
||||
String CLIENT_DETAIL_XPATH = "//button[contains(@title, 'STS701_S002_I009')]";
|
||||
String TREE_CREDIT_CARD_XPATH = "//span[contains(text(), 'Kredit')]";
|
||||
String CLIENT_CONSENTS_BUTTON = "XXX702_T001_P046";
|
||||
String SALE_BUTTON = "//button[contains(@title, '[XXX702_T001_I010]')]";
|
||||
String SALE_TAB = "XXX702_T001_L337";
|
||||
String CONTACT_INFORMATION = "//button [contains(@title, '[XXX702_T001_I011]')]";
|
||||
String ADDRESS_TABLE = "XXX702_T001_G003_UfoTable";
|
||||
String OFFERS_TABLE = "//button[contains(@title, '[XXX702_T001_I014]')]";
|
||||
String OFFERS_CHECK_BOX = "XXX702_T001_H001_AKT";
|
||||
String DEMAND_TABLE = "//button[contains(@title, '[XXX702_T001_I015]')]";
|
||||
String DEMAND_DESCRIPTION = "XXX702_T001_L269-1";
|
||||
String DEMAND_DESCRIPTION_SECOND_ROW = "XXX702_T001_L269-2";
|
||||
String TASKS_TABLE = "//button[contains(@title, '[XXX702_T001_I016]')]";
|
||||
String TASKS_LABLE = "XXX702_T001_G009_UfoTable_clone";
|
||||
String REQUEST_TABLE = "//button[contains(@title, '[XXX702_T001_I018')]";
|
||||
String REQUEST_RADIO = "XXX702_T001_R008_1";
|
||||
String NOTIFICATION_TABLE = "//button[contains(@title, '[XXX702_T001_I019]')]";
|
||||
String NOTIFICATION_LABEL = "XXX702_T001_L226";
|
||||
String FUTURE_OPPORTUNITY = "//button[contains(@title, '[STS701_S002_I003]')]";
|
||||
String OPPORTUNITY_TABLE = "STS701_S002_G011_clone";
|
||||
String RETENTION = "//button[contains(@title, '[STS701_S002_I004]')]";
|
||||
@ -122,7 +94,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
String NUMBER_OF_CARD_DIV = "STS701_S002_L110";
|
||||
String FIRST_CURRENT_ACCOUNT_COMBOBOX = "STS701_S002_C004";
|
||||
String CHECK_ACCOUNT_STATE = "STS701_S002_L174";
|
||||
String REFRESH_REQUIREMENT_BUTTON = "XXX702_T001_B001";
|
||||
String BANK_BACK_OFFICE_BUTTON = "STS701_S002_I017";
|
||||
String TRANSFER_TO_530 = "STS701_S002_I106";
|
||||
String TRANSFER_TO_535 = "STS701_S002_I108";
|
||||
@ -133,31 +104,23 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
String INFO_SERVICE_SPAN = "//span[contains(text(), 'InfoServis')]";
|
||||
String BANK_CLIENT_SPAN = "//span[contains(text(), 'BankKlient')]";
|
||||
String SMART_BANK_SPAN = "//span[contains(text(), 'Smart Banka')]";
|
||||
String CANCEL_ADC_PRODUCT_DIV = "XXX702_T001_L107";
|
||||
String INTERNET_BANK_DISPO_XPATH_CLIENT_SPAN = "//span[contains(text(), 'Internet Banka heslo/MK - Dispomodel (Aktivní)')]";
|
||||
String INTERNET_BANK_DISPO_XPATH = "//*[@id=\"STS701_S002_T001\"]/div/div[3]/div[2]/div[1]/div/span/span";
|
||||
String SMART_BANK_DISPO_XPATH = "//*[@id=\"STS701_S002_T001\"]/div/div[3]/div[2]/div[2]/div/span/span";
|
||||
String DETAIL_OF_ADC_PRODUCT = "STS701_S002_B007";
|
||||
String PLACE_OF_BUSINESS = "STS701_S002_L093";
|
||||
|
||||
String BACK_BUTTON = "HPO703_S001_S014";
|
||||
String LOANS_BUTTON_XPATH = "//button[contains(@title, '[STS701_S002_I064]')]";
|
||||
String RC_OF_HOLDER_LABEL = "STS701_S002_L157";
|
||||
String NUMBER_OF_LOANS_ACCOUNT = "STS701_S002_L269";
|
||||
String REQUEST_LIST = "//button[contains(@title, '[XXX702_T001_I015]')]";
|
||||
String FIRST_REQUEST_DONE_DIV = "XXX702_T001_L312-1";
|
||||
String COMPLAINT_BUTTON = "STS701_S002_I158";
|
||||
String COMPLAINT_BUTTON = "STS701_S002_I126";
|
||||
String COMPLAINTS_AND_OTHER_CLAIMS_BUTTON = "STS701_S002_I139";
|
||||
String PRINT_OF_LETTER = "STS701_S002_I049";
|
||||
String CONFIRMATION_ABOUT_ACCOUNT = "STS701_S002_I137";
|
||||
String DATE_IN_REQUIREMENT_DIV = "XXX702_T001_L312-1";
|
||||
String RELATIONSHIP_LABEL = "XXX702_T001_L230";
|
||||
String ACCOUNT_BLOCKING_DIV = "STS701_S002_I156";
|
||||
String TRANSACTIONS_SETTINGS = "STS701_S002_I093";
|
||||
String PAYMENTS_THROUGH_MOTO_INTERNET_DIV = "STS701_S002_L145";
|
||||
String DEBIT_CARD_SENDING_ADDRESS_STREET_LABEL = "STS701_S002_L160";
|
||||
|
||||
|
||||
|
||||
@Click(SENDING_OFFERS_MANUALS)
|
||||
ZAS701_S002_SendingOffersAndManualsPage clickSendingOffers();
|
||||
|
||||
@ -285,7 +248,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage storeAccountNumber() {
|
||||
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String accountNumber = endpoint.getText(TREE_ACCOUNT_XPATH, Lookup.XPATH).replaceAll(".*\\((\\d+);.*", "$1");
|
||||
this.store(CURRENT_ACCOUNT_NUMBER_KEY, accountNumber);
|
||||
@ -322,30 +284,12 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@CheckElementPresent(FRAUD_TABLE)
|
||||
STS701_S002_ClientDetailPage assertFraudTable();
|
||||
|
||||
@CheckElementPresent(NOTIFICATION_LABEL)
|
||||
STS701_S002_ClientDetailPage assertNotice();
|
||||
|
||||
@CheckElementPresent(CREDIT_CARD_CONTRACT_STATE_LABEL)
|
||||
STS701_S002_ClientDetailPage assertCreditCardContractState();
|
||||
|
||||
@CheckElementPresent(RETENTION_TABLE)
|
||||
STS701_S002_ClientDetailPage assertRetentionTable();
|
||||
|
||||
@CheckElementPresent(REQUEST_RADIO)
|
||||
STS701_S002_ClientDetailPage assertRequestRadio();
|
||||
|
||||
@CheckElementPresent(TASKS_LABLE)
|
||||
STS701_S002_ClientDetailPage assetTasksTable();
|
||||
|
||||
@CheckElementPresent(DEMAND_DESCRIPTION)
|
||||
STS701_S002_ClientDetailPage assertDemandDescription();
|
||||
|
||||
@CheckElementPresent(OFFERS_CHECK_BOX)
|
||||
STS701_S002_ClientDetailPage assertOffers();
|
||||
|
||||
@CheckElementPresent(ADDRESS_TABLE)
|
||||
STS701_S002_ClientDetailPage assertAddress();
|
||||
|
||||
@CheckElementPresent(OPPORTUNITY_TABLE)
|
||||
STS701_S002_ClientDetailPage assertOpportunityTable();
|
||||
|
||||
@ -369,13 +313,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@CheckElementContent("STS701_S002_L833")
|
||||
STS701_S002_ClientDetailPage assertAdcProductList(String adcProductList);
|
||||
|
||||
@Wait(value = SALE_TAB, until = Until.VISIBLE)
|
||||
@CheckElementContent("XXX702_T001_L337")
|
||||
STS701_S002_ClientDetailPage assertTabPanelSale(String saleTabPanel);
|
||||
|
||||
@CheckElementContent(RC_DIV)
|
||||
STS701_S002_ClientDetailPage checkClientRc(String rc);
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage clickTreeCreditCard() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
@ -405,31 +342,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@Click(value = FUTURE_OPPORTUNITY, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickFutureOpportunity();
|
||||
|
||||
@Click(value = NOTIFICATION_TABLE, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickNotification();
|
||||
|
||||
@Click(value = REQUEST_TABLE, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickRequests();
|
||||
|
||||
@Click(value = TASKS_TABLE, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickTasks();
|
||||
|
||||
@Click(value = DEMAND_TABLE, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME))
|
||||
STS701_S002_ClientDetailPage clickDemand();
|
||||
|
||||
@Click(value = OFFERS_TABLE, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickOffers();
|
||||
|
||||
@Click(value = CONTACT_INFORMATION, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickContactInformation();
|
||||
|
||||
@Click(value = SALE_BUTTON, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickSaleButton();
|
||||
|
||||
@Wait(value = CLIENT_CONSENTS_BUTTON, until = Until.CLICKABLE)
|
||||
@Click(CLIENT_CONSENTS_BUTTON)
|
||||
STO704_S001_ClientConsentsPage clickClientConsents();
|
||||
|
||||
@Click(value = CLIENT_DETAIL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
STS701_S002_ClientDetailPage clickClientDetailList();
|
||||
|
||||
@ -452,18 +364,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@Click(CREATE_NEW_BUTTON)
|
||||
STO702_S002_ApproveAlreadyReadPage confirmAddProduct();
|
||||
|
||||
@Click(value = APPLICATIONS_BUTTON, by = Lookup.XPATH)
|
||||
ApplicationListPage openApplicationList();
|
||||
|
||||
@Click(value = BACK_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
STS701_S002_ClientDetailPage clickBack();
|
||||
|
||||
@Click(LIST_CARDS_BUTTON)
|
||||
STS701_S002_ClientDetailPage clickCards();
|
||||
|
||||
@Click(BACK_LIST_CARDS_BUTTON)
|
||||
STS701_S002_ClientDetailPage clickBackListCards();
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME)
|
||||
@Click(CHANGE_DISTRIBUTION_BUTTON)
|
||||
STS701_S002_ClientDetailPage clickDistribution();
|
||||
@ -501,17 +401,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@CheckElementContent(CHECK_ACCOUNT_STATE)
|
||||
STS701_S002_ClientDetailPage checkAccountState(String state);
|
||||
|
||||
@CheckElementContent(DEMAND_DESCRIPTION)
|
||||
STS701_S002_ClientDetailPage checkDemandDescriptionFirstRow(String description);
|
||||
|
||||
@CheckElementContent(DEMAND_DESCRIPTION_SECOND_ROW)
|
||||
STS701_S002_ClientDetailPage checkDemandDescriptionSecondRow(String description);
|
||||
|
||||
|
||||
|
||||
@Click(value = REFRESH_REQUIREMENT_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME))
|
||||
STS701_S002_ClientDetailPage clickRefreshRequirementButton();
|
||||
|
||||
@Wait(value = INTERNET_BANK_DISPO_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = INTERNET_BANK_DISPO_XPATH, by = Lookup.XPATH)
|
||||
STS701_S002_ClientDetailPage clickIB();
|
||||
@ -545,15 +434,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@Click(value = ACCOUNT_BENEFITS)
|
||||
RET701_S003_AccountBenefits clickAccountBenefits();
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage acceptAlertIsVisible() {
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
if (endpoint.isAlertPresent(10)) {
|
||||
endpoint.acceptAlert(5);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@AcceptAlert(waitSecondsForAlert = 10, expectedAlertText = "Uzavřený účet s debetním zůstatkem")
|
||||
STS701_S002_ClientDetailPage acceptAlert();
|
||||
|
||||
@ -564,6 +444,7 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
default UKO735_S001_ClaimsAndComplaints clickComplaint() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
endpoint.click(() -> COMPLAINT_BUTTON);
|
||||
endpoint.click(() -> COMPLAINTS_AND_OTHER_CLAIMS_BUTTON);
|
||||
endpoint.focusToNewPopup(10);
|
||||
return null;
|
||||
}
|
||||
@ -602,10 +483,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
@ExecuteJavaScript("navigator.clipboard.writeText(document.getElementById('" + NUMBER_OF_LOANS_ACCOUNT + "').textContent)")
|
||||
STS701_S002_ClientDetailPage copyLoansNumberToClipboard();
|
||||
|
||||
@Wait(value = CIF_DIV, until = Until.VISIBLE)
|
||||
@ExecuteJavaScript("navigator.clipboard.writeText(document.getElementById('" + CIF_DIV + "').textContent)")
|
||||
STS701_S002_ClientDetailPage copyCifNumberToClipboard();
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_AdcProductsPanel clickSmartBank() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
@ -615,9 +492,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
return null;
|
||||
}
|
||||
|
||||
@CheckElementContent(CANCEL_ADC_PRODUCT_DIV)
|
||||
STS701_S002_ClientDetailPage checkCancelAdcProduct(String content);
|
||||
|
||||
@CheckElementContent(PLACE_OF_BUSINESS)
|
||||
STS701_S002_ClientDetailPage checkPlaceOfBusiness(String place);
|
||||
|
||||
@ -642,50 +516,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
return null;
|
||||
}
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage checkIfStatusBarIsOverwritten(String textInStatusBar) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String fullText = endpoint.getText(CANCEL_ADC_PRODUCT_DIV);
|
||||
String textWithoutTime = fullText.replaceFirst("^\\d{2}:\\d{2}:\\d{2}", "").replaceFirst("(ID:).*$", "");
|
||||
Assertions.assertTrue(textWithoutTime.contains(textInStatusBar));
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage checkDateInRequirement() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String dateInReq = endpoint.getText(DATE_IN_REQUIREMENT_DIV);
|
||||
String dateInReqNoTime = dateInReq.replaceFirst("\\s\\d{1,2}:\\d{2}:\\d{2}", "");
|
||||
Assertions.assertTrue(dateInReqNoTime.contains(endpoint.getText(ACTUAL_DATE_DIV)));
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage checkStatusMessageSendingManuals(String expectedStatusMessage) {
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
endpoint.waitForElementsToLoad(10, Until.VISIBLE, CANCEL_ADC_PRODUCT_DIV);
|
||||
String fullText = endpoint.getText(CANCEL_ADC_PRODUCT_DIV);
|
||||
String textWithoutTime = fullText.replaceFirst("^\\d{2}:\\d{2}:\\d{2}", "");
|
||||
Assertions.assertTrue(textWithoutTime.contains(expectedStatusMessage));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Click(value = REQUEST_LIST, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME))
|
||||
STS701_S002_ClientDetailPage clickRequestsList();
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage checkPerformedRequest(String expectedDescription) {
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm:ss");
|
||||
LocalTime fistTime = LocalTime.parse(endpoint.getText(CANCEL_ADC_PRODUCT_DIV).split(" ")[0], formatter);
|
||||
LocalTime secondTime = LocalTime.parse(endpoint.getText(FIRST_REQUEST_DONE_DIV).split(" ")[1], formatter);
|
||||
long diffInSeconds = Duration.between(fistTime, secondTime).getSeconds();
|
||||
Assertions.assertTrue(diffInSeconds <= 1);
|
||||
Assertions.assertEquals(endpoint.getText(DEMAND_DESCRIPTION), expectedDescription);
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage scrollToSendingOffersManuals() {
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
@ -693,13 +523,6 @@ public interface STS701_S002_ClientDetailPage extends StoreAccessor, WebFlow<STS
|
||||
return null;
|
||||
}
|
||||
|
||||
@CheckElementContent(RELATIONSHIP_LABEL)
|
||||
STS701_S002_ClientDetailPage assertRelationship(String relationship);
|
||||
|
||||
@Wait(value = CIF_DIV, until = Until.VISIBLE)
|
||||
@StoreElementContent(storeKey = CIF_STORE_KEY, value = CIF_DIV)
|
||||
STS701_S002_ClientDetailPage storeCifNumber();
|
||||
|
||||
@CustomAction
|
||||
default STS701_S002_ClientDetailPage checkInfoServiceProductIsNotPresent() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.client.clientDetail;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
@ -10,7 +11,6 @@ import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_D
|
||||
public interface STS701_S002_InfoServiceOrdersList extends StoreAccessor {
|
||||
|
||||
String CANCEL_INFO_SERVICE_BUTTON = "STS701_S002_B081";
|
||||
String CANCEL_INFO_SERVICE_DIV = "XXX702_T001_L107";
|
||||
String ORDER_DETAIL_ACCOUNTS_BUTTON = "STS701_S002_P012-%s";
|
||||
String ORDER_DETAIL_CARDS_BUTTON = "STS701_S002_P014-%s";
|
||||
String ORDER_DETAIL_OTHERS_BUTTON = "STS701_S002_P016-%s";
|
||||
@ -28,7 +28,7 @@ public interface STS701_S002_InfoServiceOrdersList extends StoreAccessor {
|
||||
endpoint.acceptAlert(5);
|
||||
endpoint.waitForElementsToLoad(5, Lookup.CLASSNAME, Until.GONE, LOAD_IMG_DIV_CLASS);
|
||||
endpoint.sendKeysOneAtATime(Key.PAGE_UP);
|
||||
endpoint.checkElementContent(CANCEL_INFO_SERVICE_DIV, "Zrušení objednávek proběhlo v pořádku.");
|
||||
endpoint.checkElementContent(XXX702_SharedTopMenu.STATUS_BAR, "Zrušení objednávek proběhlo v pořádku.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package cz.moneta.test.dsl.ufo.operations.client.product;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_AdcProductsPanel;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
@ -10,20 +11,15 @@ import org.junit.Assert;
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
@Wait(value = ADC739_S001_IBSBPage.CLIENTS_NAME, until = Until.VISIBLE)
|
||||
public interface ADC739_S001_IBSBPage extends WebFlow<ADC739_S001_IBSBPage>, StoreAccessor {
|
||||
public interface ADC739_S001_IBSBPage extends WebFlow<ADC739_S001_IBSBPage>, XXX702_SharedTopMenu<ADC739_S001_IBSBPage>, StoreAccessor {
|
||||
|
||||
String DETAIL_EDISPONENT = "ADC739_S001_P001-1";
|
||||
String CLIENTS_NAME = "ADC739_S001_L004-1";
|
||||
String SEND_LOGIN_PASSWORD_SMS = "ADC739_S001_B003";
|
||||
String STATUS_BAR = "XXX702_T001_L107";
|
||||
String REQUIREMENT_BUTTON_XPATH = "//*[@id=\"XXX702_T001_V002\"]/div[2]/div/div[5]/div/button";
|
||||
String REFRESH_REQUIREMENT_BUTTON = "XXX702_T001_B001";
|
||||
String REQUIREMENT_DESCRIPTION = "XXX702_T001_L269-1";
|
||||
String SEND_ID_FOR_IB_SMS = "ADC739_S001_B005";
|
||||
String PHISING_SMS_BUTTON = "ADC739_S001_B008";
|
||||
String BLOCATION_ALL_PROFILES_BUTTON = "ADC739_S001_B001";
|
||||
String UNBLOCATION_ALL_PROFILES_BUTTON = "ADC739_S001_B019";
|
||||
String DETAIL_OF_CLIENT_BUTTON = "XXX702_T001_B016";
|
||||
String BLOCATION_OF_IB_CANAL_BUTTON = "ADC739_S001_B012";
|
||||
String STATE_OF_IB_CANAL = "ADC739_S001_L104";
|
||||
String CANAL_IN_ROW_DIV = "ADC739_S001_L077-1";
|
||||
@ -44,24 +40,12 @@ public interface ADC739_S001_IBSBPage extends WebFlow<ADC739_S001_IBSBPage>, Sto
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
@CustomAction
|
||||
default ADC739_S001_IBSBPage checkIfStatusBarIsOverwritten(String textInStatusBar) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String fullText = endpoint.getText(STATUS_BAR);
|
||||
String fullText = getStatusBarContent();
|
||||
String textWithoutTime = fullText.replaceFirst("^\\d{2}:\\d{2}:\\d{2}", "");
|
||||
Assert.assertTrue(textWithoutTime.contains(textInStatusBar));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Wait(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
ADC739_S001_IBSBPage clickRequirement();
|
||||
|
||||
@Wait(value = REFRESH_REQUIREMENT_BUTTON, until = Until.CLICKABLE)
|
||||
@Click(value = REFRESH_REQUIREMENT_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
ADC739_S001_IBSBPage clickRefreshRequirement();
|
||||
|
||||
@CheckElementContent(REQUIREMENT_DESCRIPTION)
|
||||
ADC739_S001_IBSBPage checkRequirementDescription(String description);
|
||||
|
||||
@Wait(value = SEND_ID_FOR_IB_SMS, until = Until.CLICKABLE)
|
||||
@Click(SEND_ID_FOR_IB_SMS)
|
||||
@AcceptAlert(waitSecondsForAlert = 15, expectedAlertText = "Souhlasíte, aby Vám společnost koncernu MONETA Money Bank, a. s. jednorázově zaslala ID k Internet Bance na telefonní číslo mobilního klíče")
|
||||
@ -76,10 +60,6 @@ public interface ADC739_S001_IBSBPage extends WebFlow<ADC739_S001_IBSBPage>, Sto
|
||||
@Click(value = BLOCATION_ALL_PROFILES_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
ADC739_S001_IBSBPage clickBlockationAllProfilesButton();
|
||||
|
||||
@Wait(value = DETAIL_OF_CLIENT_BUTTON, until = Until.CLICKABLE)
|
||||
@Click(value = DETAIL_OF_CLIENT_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
STS701_S002_ClientDetailPage clickDetailOfClientButton();
|
||||
|
||||
@Wait(value = UNBLOCATION_ALL_PROFILES_BUTTON, until = Until.CLICKABLE)
|
||||
@Click(UNBLOCATION_ALL_PROFILES_BUTTON)
|
||||
@AcceptAlert(waitSecondsForAlert = 15, expectedAlertText = "ADC kanál blokován na žádost banky = podezření na podvod! Ověřil si, že nemůže dojít k zneužití údajů klienta (IB a SB)? Opravdu chcete kanál odblokovat?")
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.CheckElementContent;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.Wait;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface XXX702_Applications<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String APPLICATIONS_RADIO = "XXX702_T001_R008_1";
|
||||
|
||||
String LOAD_BUTTON = "XXX702_T001_B026";
|
||||
String LOAD_ALL_BUTTON = "XXX702_T001_B027";
|
||||
String APPLICATION_COPY_BUTTON = "XXX702_T001_B034";
|
||||
String FINISH_APPLICATION_BUTTON = "XXX702_T001_B029";
|
||||
String APPLICATION_DECISION_BUTTON = "XXX702_T001_B030";
|
||||
String STORNO_APPLICATION_BUTTON = "XXX702_T001_B028";
|
||||
|
||||
String FIRST_APPLICATION_DETAIL_BUTTON = "XXX702_T001_P045-1";
|
||||
String FIRST_APPLICATION_STATE_LABEL = "XXX702_T001_L285-1";
|
||||
String FIRST_APPLICATION_REQUESTED_PRODUCT_CODE_LABEL = "XXX702_T001_L303-1";
|
||||
String FIRST_APPLICATION_REQUESTED_AMOUNT_LABEL = "XXX702_T001_L305-1";
|
||||
String FIRST_APPLICATION_APPROVED_AMOUNT_LABEL = "XXX702_T001_L306-1";
|
||||
String FIRST_APPLICATION_CHANNEL_LABEL = "XXX702_T001_L310-1";
|
||||
|
||||
@CheckElementPresent(APPLICATIONS_RADIO)
|
||||
T checkApplicationsRadioPresence();
|
||||
|
||||
@CheckElementPresent(LOAD_BUTTON)
|
||||
T checkApplicationsLoadButtonPresence();
|
||||
|
||||
@CheckElementPresent(LOAD_ALL_BUTTON)
|
||||
T checkApplicationsLoadAllButtonPresence();
|
||||
|
||||
@CheckElementPresent(APPLICATION_COPY_BUTTON)
|
||||
T checkApplicationCopyButtonPresence();
|
||||
|
||||
@CheckElementPresent(FINISH_APPLICATION_BUTTON)
|
||||
T checkApplicationFinishButtonPresence();
|
||||
|
||||
@CheckElementPresent(APPLICATION_DECISION_BUTTON)
|
||||
T checkApplicationDecisionButtonPresence();
|
||||
|
||||
@CheckElementPresent(STORNO_APPLICATION_BUTTON)
|
||||
T checkApplicationStornoButtonPresence();
|
||||
|
||||
|
||||
@CheckElementPresent(FIRST_APPLICATION_DETAIL_BUTTON)
|
||||
T checkApplicationsFirstDetailButtonPresence();
|
||||
|
||||
@Wait(explicitWaitSeconds = 1) // There is a moment when list is full of some random items, can't think of better way to track it
|
||||
@CheckElementContent(FIRST_APPLICATION_STATE_LABEL)
|
||||
T assertFirstApplicationState(String firstApplicationState);
|
||||
|
||||
@CheckElementContent(FIRST_APPLICATION_REQUESTED_PRODUCT_CODE_LABEL)
|
||||
T assertFirstApplicationProductCode(String expectedProductCode);
|
||||
|
||||
@CheckElementContent(FIRST_APPLICATION_REQUESTED_AMOUNT_LABEL)
|
||||
T assertFirstApplicationRequestedAmount(String expectedAmount);
|
||||
|
||||
@CheckElementContent(FIRST_APPLICATION_APPROVED_AMOUNT_LABEL)
|
||||
T assertFirstApplicationApprovedAmount(String expectedAmount);
|
||||
|
||||
@CheckElementContent(FIRST_APPLICATION_CHANNEL_LABEL)
|
||||
T assertFirstApplicationChannel(String channel);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
public interface XXX702_ClientDetail<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String CLIENT_CIF_LABEL = "XXX702_T002_L101";
|
||||
String CLIENT_RC_LABEL = "XXX702_T001_L009";
|
||||
String CONSENTS_101_MMB_LABEL = "XXX702_T001_L261";
|
||||
String RELATIONSHIP_LABEL = "XXX702_T001_L230";
|
||||
|
||||
@CheckElementContent(CLIENT_RC_LABEL)
|
||||
T assertClientRc(String rc);
|
||||
|
||||
@CheckElementContent(RELATIONSHIP_LABEL)
|
||||
T assertClientRelationship(String relationship);
|
||||
|
||||
@Wait(value = CLIENT_CIF_LABEL, until = Until.VISIBLE)
|
||||
@ExecuteJavaScript("navigator.clipboard.writeText(document.getElementById('" + CLIENT_CIF_LABEL + "').textContent)")
|
||||
T copyCifNumberToClipboard();
|
||||
|
||||
@Wait(value = CONSENTS_101_MMB_LABEL)
|
||||
@CustomAction
|
||||
default T checkClientConsentsMMBTitle(String expectedTitle) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
Assertions.assertEquals(expectedTitle, endpoint.getAttribute(CONSENTS_101_MMB_LABEL, "title"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface XXX702_ContactInformations<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String ADDRESS_TABLE = "XXX702_T001_G003_UfoTable";
|
||||
|
||||
@CheckElementPresent(ADDRESS_TABLE)
|
||||
STS701_S002_ClientDetailPage checkAddressTablePresence();
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface XXX702_Notices<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String NOTICES_TITLE_LABEL = "XXX702_T001_L226";
|
||||
|
||||
@CheckElementPresent(NOTICES_TITLE_LABEL)
|
||||
T checkNoticesTitlePresence();
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface XXX702_Offers<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String ACTIVE_OFFERS_CHECKBOX = "XXX702_T001_H001_AKT";
|
||||
|
||||
@CheckElementPresent(ACTIVE_OFFERS_CHECKBOX)
|
||||
T checkActiveOffersCheckboxPresence();
|
||||
|
||||
}
|
||||
@ -1,22 +1,56 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.recorder.CRC710_S001_RecorderMainPage;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
public interface XXX702_Requirements<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String REQUIREMENT_DATE = "XXX702_T001_L286-1";
|
||||
String FIRST_REQUIREMENT_DESCRIPTION = "XXX702_T001_L269-1";
|
||||
String FIRST_REQUIREMENT_SUBMIT_DATE = "XXX702_T001_L286-1";
|
||||
String FIRST_REQUIREMENT_SOLVE_DATE = "XXX702_T001_L312-1";
|
||||
String FIRST_REQUIREMENT_DETAIL_BUTTON = "XXX702_T001_P035-1";
|
||||
|
||||
String SECOND_REQUIREMENT_DESCRIPTION = "XXX702_T001_L269-2";
|
||||
|
||||
String REFRESH_REQ_BUTTON = "XXX702_T001_B001";
|
||||
|
||||
@Wait(value = REQUIREMENT_DATE, until = Until.VISIBLE)
|
||||
@GetElementContent(REQUIREMENT_DATE)
|
||||
String getRequirementDate();
|
||||
@CheckElementPresent(FIRST_REQUIREMENT_DESCRIPTION)
|
||||
T checkFirstRequirementDescriptionPresence();
|
||||
|
||||
|
||||
@CheckElementContent(FIRST_REQUIREMENT_DESCRIPTION)
|
||||
T assertFirstRequirementDescription(String description);
|
||||
|
||||
@CheckElementContent(FIRST_REQUIREMENT_SOLVE_DATE)
|
||||
T assertFirstRequirementSolveDate(String expectedDate);
|
||||
|
||||
|
||||
@GetElementContent(FIRST_REQUIREMENT_SUBMIT_DATE)
|
||||
String getFirstRequirementSubmitDate();
|
||||
|
||||
@GetElementContent(FIRST_REQUIREMENT_SOLVE_DATE)
|
||||
String getFirstRequirementSolveDate();
|
||||
|
||||
@CustomAction
|
||||
default CRC710_S001_RecorderMainPage clickFirstRequirementDetail() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
endpoint.waitForElementsToLoad(15, FIRST_REQUIREMENT_DETAIL_BUTTON);
|
||||
endpoint.click(() -> FIRST_REQUIREMENT_DETAIL_BUTTON);
|
||||
endpoint.focusToNewPopup(5);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@CheckElementContent(SECOND_REQUIREMENT_DESCRIPTION)
|
||||
T assertSecondRequirementDescription(String description);
|
||||
|
||||
|
||||
@Wait(value = REFRESH_REQ_BUTTON, until = Until.VISIBLE)
|
||||
@Click(value = REFRESH_REQ_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Requirements<T> clickRefreshRequirement();
|
||||
XXX702_Requirements<T> clickRefreshRequirements();
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
public interface XXX702_Sale<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String EXPRESS_LABEL = "XXX702_T001_L337";
|
||||
|
||||
@CheckElementPresent(EXPRESS_LABEL)
|
||||
T checkExpressLabelPresence();
|
||||
|
||||
}
|
||||
@ -1,19 +1,68 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.ufoOperations.STO704_S001_ClientConsentsPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.ufoOperations.ZKU704_S001_ChangeContactData;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
public interface XXX702_SharedClientMenu<T extends WebFlow<T>> extends XXX702_Requirements<T> {
|
||||
public interface XXX702_SharedClientMenu<T extends WebFlow<T>> extends XXX702_ClientDetail<T>, XXX702_Sale<T>, XXX702_ContactInformations<T>, XXX702_Offers<T>, XXX702_Requirements<T>, XXX702_Tasks<T>, XXX702_Applications<T>, XXX702_Notices<T> {
|
||||
|
||||
String REQUIREMENT_BUTTON_XPATH = "//button[contains(@title, '[XXX702_T001_I015]')]";
|
||||
String CLIENT_DETAIL_PANEL_XPATH = "//button [contains(@title, '[XXX702_T001_I006]')]";
|
||||
String SALE_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I010]')]";
|
||||
String CONTACT_INFORMATIONS_PANEL_XPATH = "//button [contains(@title, '[XXX702_T001_I011]')]";
|
||||
String OFFERS_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I014]')]";
|
||||
String REQUIREMENT_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I015]')]";
|
||||
String TASKS_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I016]')]";
|
||||
String APPLICATIONS_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I018')]";
|
||||
String NOTICES_PANEL_XPATH = "//button[contains(@title, '[XXX702_T001_I019]')]";
|
||||
|
||||
String CLIENT_DETAIL_BUTTON = "XXX702_T001_B016";
|
||||
String CLIENT_CONSENTS_BUTTON = "XXX702_T001_P046";
|
||||
String CHANGE_CONTACT_DATA_BUTTON = "XXX702_T001_P047";
|
||||
|
||||
@Wait(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Requirements<T> menuClickRequirements();
|
||||
|
||||
@Wait(value = CLIENT_DETAIL_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = CLIENT_DETAIL_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_ClientDetail<T> menuClickClientDetailPanel();
|
||||
|
||||
@Wait(value = SALE_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = SALE_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Sale<T> menuClickClientSale();
|
||||
|
||||
@Wait(value = CONTACT_INFORMATIONS_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = CONTACT_INFORMATIONS_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_ContactInformations<T> menuClickClientContactInformations();
|
||||
|
||||
@Wait(value = OFFERS_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = OFFERS_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Offers<T> menuClickClientOffers();
|
||||
|
||||
@Wait(value = REQUIREMENT_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = REQUIREMENT_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Requirements<T> menuClickClientRequirements();
|
||||
|
||||
@Wait(value = TASKS_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = TASKS_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Tasks<T> menuClickClientTasks();
|
||||
|
||||
@Wait(value = APPLICATIONS_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = APPLICATIONS_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Applications<T> menuClickClientApplications();
|
||||
|
||||
@Wait(value = NOTICES_PANEL_XPATH, by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = NOTICES_PANEL_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
XXX702_Notices<T> menuClickClientNotices();
|
||||
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
@Click(value = CLIENT_DETAIL_BUTTON)
|
||||
STS701_S002_ClientDetailPage menuClickClientDetailButton();
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
@Click(CLIENT_CONSENTS_BUTTON)
|
||||
STO704_S001_ClientConsentsPage menuClickClientConsents();
|
||||
|
||||
@Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE)
|
||||
@Click(CHANGE_CONTACT_DATA_BUTTON)
|
||||
|
||||
@ -19,6 +19,8 @@ import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_D
|
||||
|
||||
public interface XXX702_SharedTopMenu<T extends WebFlow<T>> extends XXX702_SharedClientMenu<T>, XXX702_SharedSearchMenu<T>, StoreAccessor {
|
||||
|
||||
String STATUS_BAR = "XXX702_T001_L107";
|
||||
|
||||
String LOGOUT_BUTTON = "XXX702_T001_B008";
|
||||
|
||||
String ACTUAL_DATE_DIV = "XXX702_T001_L172";
|
||||
@ -33,6 +35,20 @@ public interface XXX702_SharedTopMenu<T extends WebFlow<T>> extends XXX702_Share
|
||||
String CALCULATOR_BUTTON = "XXX702_T001_P010";
|
||||
String TASK_MANAGEMENT_BUTTON = "XXX702_T001_P053";
|
||||
|
||||
@GetElementContent(STATUS_BAR)
|
||||
String getStatusBarContent();
|
||||
|
||||
@CheckElementContent(STATUS_BAR)
|
||||
T assertStatusBarContent(String expectedText);
|
||||
|
||||
@CustomAction
|
||||
default T checkStatusBarIsEmpty() {
|
||||
String statusBarContent = getEndpoint(UfoEndpoint.class).getText(STATUS_BAR);
|
||||
Assertions.assertTrue(statusBarContent == null || statusBarContent.isEmpty(), "Expected status bar to be empty, but was: " + statusBarContent);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Click(LOGOUT_BUTTON)
|
||||
LogOutPage clickLogout();
|
||||
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.shared.topmenu;
|
||||
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.support.web.CheckElementPresent;
|
||||
import cz.moneta.test.harness.support.web.WebFlow;
|
||||
|
||||
public interface XXX702_Tasks<T extends WebFlow<T>> extends WebFlow<T>, StoreAccessor {
|
||||
|
||||
String TASKS_TABLE = "XXX702_T001_G009_UfoTable_clone";
|
||||
|
||||
@CheckElementPresent(TASKS_TABLE)
|
||||
T checkTasksTablePresence();
|
||||
|
||||
}
|
||||
@ -84,7 +84,7 @@ public interface ETS701_S001_ClientDocuments extends WebFlow<ETS701_S001_ClientD
|
||||
default ETS701_S001_ClientDocuments checkDistributionAndGetTime() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d.M.yyyy H:mm:ss");
|
||||
String requirementDate = menuClickRequirements().clickRefreshRequirement().getRequirementDate();
|
||||
String requirementDate = menuClickClientRequirements().clickRefreshRequirements().getFirstRequirementSubmitDate();
|
||||
endpoint.waitForElementsToLoad(10, CORRESPONDENCE_DATE);
|
||||
String correspondenceDate = endpoint.getText(CORRESPONDENCE_DATE);
|
||||
LocalDateTime reqDate = LocalDateTime.parse(requirementDate, formatter).withMinute(0).withSecond(0).withNano(0);
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.ufoOperations;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
public interface PLK703_S002_DebitCardBlocationPage extends WebFlow<PLK703_S002_DebitCardBlocationPage> {
|
||||
public interface PLK703_S002_DebitCardBlocationPage extends WebFlow<PLK703_S002_DebitCardBlocationPage>, XXX702_SharedTopMenu<PLK703_S002_DebitCardBlocationPage> {
|
||||
|
||||
String SAVE_BUTTON = "PLK703_S002_B003";
|
||||
String BACK_DART_BUTTON = "PLK703_S002_B001";
|
||||
@ -18,10 +19,6 @@ public interface PLK703_S002_DebitCardBlocationPage extends WebFlow<PLK703_S002_
|
||||
String SUBMIT_SAVE_BUTTON = "PLK703_S002_B003";
|
||||
String UNBLOCK_DEBIT_CARD_BUTTON = "PLK703_S002_B004";
|
||||
String CONFIRM_BLOCK_BUTTON = "HLA701_S001_B002";
|
||||
String REQUIREMENT_BUTTON_XPATH = "//button[contains(@title, '[XXX702_T001_I015]')]";
|
||||
|
||||
String REFRESH_BUTTON = "XXX702_T001_B001";
|
||||
String REQ_TO_CONTROL = "XXX702_T001_L269-1";
|
||||
String CONFIRM_ALERT_BUTTON = "HLA701_S001_B001";
|
||||
String TELEPHONE_NUMBER_TEXTBOX = "PLK703_S002_E007";
|
||||
|
||||
@ -58,21 +55,6 @@ public interface PLK703_S002_DebitCardBlocationPage extends WebFlow<PLK703_S002_
|
||||
@Click(value = CONFIRM_BLOCK_BUTTON)
|
||||
PLK703_S002_DebitCardBlocationPage clickConfirmBlockButton();
|
||||
|
||||
@Wait(value = REQUIREMENT_BUTTON_XPATH,by = Lookup.XPATH, until = Until.CLICKABLE)
|
||||
@Click(value = REQUIREMENT_BUTTON_XPATH,by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME))
|
||||
PLK703_S002_DebitCardBlocationPage clickRequirementButton();
|
||||
|
||||
@Wait(value = REFRESH_BUTTON, until = Until.CLICKABLE)
|
||||
@Click(value = REFRESH_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, until = Until.GONE, by = Lookup.CLASSNAME))
|
||||
PLK703_S002_DebitCardBlocationPage clickRefreshButton();
|
||||
|
||||
@Wait(value = REQ_TO_CONTROL, waitSecondsForElement = 30)
|
||||
@CheckElementContent(value = REQ_TO_CONTROL)
|
||||
PLK703_S002_DebitCardBlocationPage assertReqTempBlock(String text);
|
||||
|
||||
@CheckElementContent(value = REQ_TO_CONTROL)
|
||||
PLK703_S002_DebitCardBlocationPage assertReqTempUnblock(String text);
|
||||
|
||||
@Click(CONFIRM_ALERT_BUTTON)
|
||||
PLK703_S002_DebitCardBlocationPage clickConfirmAlertButton();
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.ufoOperations;
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
import cz.moneta.test.harness.support.web.*;
|
||||
@ -10,7 +11,7 @@ import static cz.moneta.test.dsl.ufo.operations.ufoOperations.PLK708_S001_DataCh
|
||||
import static cz.moneta.test.dsl.ufo.operations.shared.SharedElements.LOAD_IMG_DIV_CLASS;
|
||||
|
||||
@Wait(value = PAGE_TITLE, until = Until.VISIBLE)
|
||||
public interface PLK708_S001_DataChangesPage extends WebFlow<PLK708_S001_DataChangesPage>, StoreAccessor {
|
||||
public interface PLK708_S001_DataChangesPage extends WebFlow<PLK708_S001_DataChangesPage>, XXX702_SharedTopMenu<PLK708_S001_DataChangesPage>, StoreAccessor {
|
||||
|
||||
String PAGE_TITLE = "PLK708el11";
|
||||
String ADDRESS_FOR_SENDING_SELECT = "PLK708_S001_C001";
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package cz.moneta.test.dsl.ufo.operations.ufoOperations;
|
||||
|
||||
|
||||
import cz.moneta.test.dsl.ufo.operations.recorder.CRC710_S001_RecorderMainPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.shared.topmenu.XXX702_SharedTopMenu;
|
||||
import cz.moneta.test.harness.context.StoreAccessor;
|
||||
import cz.moneta.test.harness.endpoints.ufo.UfoEndpoint;
|
||||
@ -48,16 +47,10 @@ public interface STO704_S001_ClientConsentsPage extends WebFlow<STO704_S001_Clie
|
||||
String SEARCH_HISTORY_BUTTON = "STO704_S001_B006";
|
||||
String DETAIL_OF_LIST_CHANGE = "STO704_S001_P002-1";
|
||||
String CONSENT_STATUS_SET = "STO704_S001_L088-1";
|
||||
String REQUIREMENT_BUTTON_XPATH = "//*[@id=\"XXX702_T001_V002\"]/div[2]/div/div[5]/div/button";
|
||||
String REFRESH_REQUIREMENT = "XXX702_T001_B001";
|
||||
String CLIENT_CONSENTS = "XXX702_T001_P046";
|
||||
String PRINT_COMBOBOX = "STO704_S001_C010";
|
||||
String GENERATE_PRINT = "STO704_S001_B008";
|
||||
String TYPE_OF_CONSENT = "STO704_S001_L077-3";
|
||||
String DETAIL_OF_REQUIREMENT = "XXX702_T001_P035-1";
|
||||
String MMB_LABEL = "XXX702_T001_L261";
|
||||
String TODAY_DATE_IN_CHANGES = "STO704_S001_E002";
|
||||
String TODAY_DATE = "XXX702_T001_L172";
|
||||
String POST_CONSENT = "STO704_S001_L077-4";
|
||||
String IBSB_CONSENT = "STO704_S001_L077-6";
|
||||
String SURVEY_CONSENT = "STO704_S001_L077-5";
|
||||
@ -164,17 +157,6 @@ public interface STO704_S001_ClientConsentsPage extends WebFlow<STO704_S001_Clie
|
||||
@CheckElementContent(CONSENT_STATUS_SET)
|
||||
STO704_S001_ClientConsentsPage checkConsentsStatusSet(String consent);
|
||||
|
||||
@Wait(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, until = Until.VISIBLE)
|
||||
@Click(value = REQUIREMENT_BUTTON_XPATH, by = Lookup.XPATH, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
STO704_S001_ClientConsentsPage clickRequirement();
|
||||
|
||||
@Wait(REFRESH_REQUIREMENT)
|
||||
@Click(value = REFRESH_REQUIREMENT, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
STO704_S001_ClientConsentsPage clickRefreshRequirement();
|
||||
|
||||
@Click(CLIENT_CONSENTS)
|
||||
STO704_S001_ClientConsentsPage clickClientConsents();
|
||||
|
||||
@Select(value = PRINT_COMBOBOX, selectByOrder = true)
|
||||
STO704_S001_ClientConsentsPage selectTypeOfPrint(int order);
|
||||
|
||||
@ -189,15 +171,6 @@ public interface STO704_S001_ClientConsentsPage extends WebFlow<STO704_S001_Clie
|
||||
@CheckElementContent(EMAIL_CONSENT)
|
||||
STO704_S001_ClientConsentsPage checkTypeOfConsentEmail(String consent);
|
||||
|
||||
@CustomAction
|
||||
default CRC710_S001_RecorderMainPage clickDetailOfRequirement() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
endpoint.waitForElementsToLoad(15, DETAIL_OF_REQUIREMENT);
|
||||
endpoint.click(() -> DETAIL_OF_REQUIREMENT);
|
||||
endpoint.focusToNewPopup(5);
|
||||
return null;
|
||||
}
|
||||
|
||||
@AcceptAlert(waitSecondsForAlert = 10, expectedAlertText = "Souhlasím se zpracováním svých osobních údajů, zejména, informací o bankovních obchodech a údajů vzniklých v průběhu využívání produktů a služeb které sdělím, nebo které oprávněným způsobem získají společnosti koncernu MONETA (MONETA Money Bank, a. s., MONETA Auto, s.r.o., MONETA Leasing, s.r.o. a MONETA Stavební Spořitelna a.s.) pro přípravu nabídky na míru a oslovení s nabídkou produktů nebo služeb. Více na www.moneta.cz v sekci Ochrana osobních údajů.")
|
||||
STO704_S001_ClientConsentsPage acceptAlert();
|
||||
|
||||
@ -269,24 +242,13 @@ public interface STO704_S001_ClientConsentsPage extends WebFlow<STO704_S001_Clie
|
||||
@CheckElementContent(OTHER_CHANNELS_MMB_CONSENTS)
|
||||
STO704_S001_ClientConsentsPage checkOtherChannelsMMBConsents(String text);
|
||||
|
||||
@Wait(value = MMB_LABEL)
|
||||
@CustomAction
|
||||
default STO704_S001_ClientConsentsPage checkIfMMBTitleIsShowingExpectedText() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String MMB_title = endpoint.getAttribute(MMB_LABEL, "title");
|
||||
String MMB_expected_title = "Telefon - PLATNÝ [XXX702_T001_L261]";
|
||||
Assertions.assertEquals(MMB_expected_title, MMB_title, "The title isn't visible");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Wait(value = TODAY_DATE, until = Until.VISIBLE)
|
||||
@Wait(value = TODAY_DATE_IN_CHANGES, until = Until.VISIBLE)
|
||||
@CustomAction
|
||||
default STO704_S001_ClientConsentsPage checkIfIsSelectedTodayDate() {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
String todayDateInChanges = endpoint.getAttribute(TODAY_DATE_IN_CHANGES, "value");
|
||||
String todayDate = endpoint.getText(TODAY_DATE);
|
||||
Assertions.assertEquals(todayDateInChanges, todayDate, "Dates aren't same.");
|
||||
String todayDate = getActualDate();
|
||||
Assertions.assertEquals(todayDate, todayDateInChanges, "Dates aren't same.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -25,8 +25,6 @@ public interface TRA740_S001_CreateNewTransferPaymentOrderPage extends WebFlow<T
|
||||
String AMOUNT_INPUT = "TRA740_S001_E005";
|
||||
|
||||
String SEND_BUTTON = "TRA740_S001_B001";
|
||||
String TRANSACTION_SUCCESSFULLY_ESTABLISHED_DIV = "XXX702_T001_L107";
|
||||
String ID_PAYMENT_ORDER_DIV = "XXX702_T001_L107";
|
||||
String VARIABLE_SYMBOL = "TRA740_S001_E007";
|
||||
String SPECIFICALLY_SYMBOL = "TRA740_S001_E009";
|
||||
String MESSAGE = "TRA740_S001_M001";
|
||||
@ -65,14 +63,10 @@ public interface TRA740_S001_CreateNewTransferPaymentOrderPage extends WebFlow<T
|
||||
@Click(value = SEND_BUTTON, andWait = @Wait(value = LOAD_IMG_DIV_CLASS, by = Lookup.CLASSNAME, until = Until.GONE))
|
||||
TRA740_S001_CreateNewTransferPaymentOrderPage clickSend();
|
||||
|
||||
@CheckElementContent(TRANSACTION_SUCCESSFULLY_ESTABLISHED_DIV)
|
||||
TRA740_S001_CreateNewTransferPaymentOrderPage checkTransactionSuccessfullyEstablished(String content);
|
||||
|
||||
@CustomAction
|
||||
default TRA740_S001_CreateNewTransferPaymentOrderPage saveTaskIDPaymentOrder() {
|
||||
|
||||
UfoEndpoint endpoint = this.getEndpoint(UfoEndpoint.class);
|
||||
String establishedTaskText = endpoint.getText(ID_PAYMENT_ORDER_DIV).replaceAll(".*ID: (\\d+)$", "$1");
|
||||
String establishedTaskText = endpoint.getText(STATUS_BAR).replaceAll(".*ID: (\\d+)$", "$1");
|
||||
this.store(ID_PAYMENT_ORDER_KEY, establishedTaskText);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -73,12 +73,8 @@ public interface XCM701_S001_MortgagesPanel extends XCM701_S001_ContactForwardin
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_MortgagesPanel checkPhone(String phone) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
Assertions.assertEquals(phone, endpoint.getAttribute(PHONE_INPUT, "value"));
|
||||
return null;
|
||||
}
|
||||
@TypeInto(value = PHONE_INPUT, clear = true)
|
||||
XCM701_S001_MortgagesPanel fillPhone(String phone);
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_MortgagesPanel checkEmail(String email) {
|
||||
|
||||
@ -90,12 +90,8 @@ public interface XCM701_S001_OtherPanel extends XCM701_S001_ContactForwardingPag
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_OtherPanel checkMobile(String mobile) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
Assertions.assertEquals(mobile, endpoint.getAttribute(MOBILE_INPUT, "value"));
|
||||
return null;
|
||||
}
|
||||
@TypeInto(value = MOBILE_INPUT, clear = true)
|
||||
XCM701_S001_OtherPanel fillMobile(String mobile);
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_OtherPanel checkEmail(String email) {
|
||||
|
||||
@ -77,12 +77,8 @@ public interface XCM701_S001_SMEPanel extends XCM701_S001_ContactForwardingPage
|
||||
return null;
|
||||
}
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_SMEPanel checkMobile(String mobile) {
|
||||
UfoEndpoint endpoint = getEndpoint(UfoEndpoint.class);
|
||||
Assertions.assertEquals(mobile, endpoint.getAttribute(MOBILE_INPUT, "value"));
|
||||
return null;
|
||||
}
|
||||
@TypeInto(value = MOBILE_INPUT, clear = true)
|
||||
XCM701_S001_SMEPanel fillMobile(String mobile);
|
||||
|
||||
@CustomAction
|
||||
default XCM701_S001_SMEPanel checkEmail(String email) {
|
||||
|
||||
@ -182,5 +182,10 @@ public class Keys {
|
||||
*/
|
||||
public static final String inheritance = "exevido-inheritance";
|
||||
|
||||
/**
|
||||
* User with role exevido.inheritanceController
|
||||
*/
|
||||
public static final String inheritanceController = "exevido-inheritanceController";
|
||||
|
||||
}
|
||||
}
|
||||
@ -207,6 +207,9 @@ public class PaymentData {
|
||||
@Setter
|
||||
private String messageId;
|
||||
|
||||
@Setter
|
||||
private String uetr;
|
||||
|
||||
@Setter
|
||||
private String message;
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import cz.moneta.test.dsl.util.task.dataprepare.DataPrepareTasks;
|
||||
import cz.moneta.test.dsl.util.task.dmbsib.DmbsibTasks;
|
||||
import cz.moneta.test.dsl.util.task.firstvision.FirstVisionTasks;
|
||||
import cz.moneta.test.dsl.util.task.ib.IbTasks;
|
||||
import cz.moneta.test.dsl.util.task.paemq.PaeMqTasks;
|
||||
import cz.moneta.test.dsl.util.task.ihub.IHubTasks;
|
||||
import cz.moneta.test.dsl.util.task.insurance.InsuranceTasks;
|
||||
import cz.moneta.test.dsl.util.task.kasanova.KSNTasks;
|
||||
@ -187,4 +188,8 @@ public class Tasks {
|
||||
public ExevidoTasks exevidoTasks() {
|
||||
return new ExevidoTasks(harness);
|
||||
}
|
||||
|
||||
public PaeMqTasks paeMq() {
|
||||
return new PaeMqTasks(harness);
|
||||
}
|
||||
}
|
||||
@ -143,7 +143,8 @@ public class CalculationTypeTasks {
|
||||
.typeVehicleInsuranceCustomerSurname(customer.getSurname())
|
||||
.typeVehicleInsuranceCustomerBirthdate(customerData.getBirthdate().getValue())
|
||||
.typeVehicleInsuranceCustomerPIN(customerData.getPin().getValue())
|
||||
.typeVehicleInsuranceCustomerNationality(customerData.getNationality().getValue())
|
||||
.clickVehicleInsuranceCustomerNationalityTextbox()
|
||||
.clickVehicleInsuranceCustomerNationalityTextbox()
|
||||
.clickVehicleInsuranceCustomerNationality()
|
||||
.typeVehicleInsuranceCustomerAddress(customer.getAddress())
|
||||
.clickVehicleInsuranceCustomerAddress();
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
package cz.moneta.test.dsl.util.task.paemq;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.payment_engine_mq.MessagingType;
|
||||
import cz.moneta.test.harness.support.rest.RawRestRequest;
|
||||
|
||||
public class PaeMqTasks {
|
||||
private final Harness harness;
|
||||
|
||||
public PaeMqTasks(Harness harness) {
|
||||
this.harness = harness;
|
||||
}
|
||||
|
||||
public RawRestRequest.Request prepareEmptyRequest(MessagingType messagingType) {
|
||||
return this.harness.withPaeMq().prepareRequest(messagingType);
|
||||
}
|
||||
|
||||
public RawRestRequest.Post prepareRequest(MessagingType messagingType, String payload) {
|
||||
return this.harness.withPaeMq().prepareRequest(messagingType)
|
||||
.withPayload(payload);
|
||||
}
|
||||
|
||||
public void uploadFile(MessagingType messagingType, String file) {
|
||||
prepareRequest(messagingType, file).post().andAssertStatus(201);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,13 +1,17 @@
|
||||
package cz.moneta.test.dsl.util.task.ufo.operations;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.STO702_S002_ApproveAlreadyReadPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.NewProductsByNameUfoOps;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.NewProductsUfoOps;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.STO702_S002_ApproveAlreadyReadPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.search.OPS001_S001_ClientSearchPageUfoOperations;
|
||||
import cz.moneta.test.dsl.ufo.operations.ufoOperations.contactForwarding.XCM701_S001_ContactForwardingPage;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ClientDetailsTasks {
|
||||
@ -32,4 +36,24 @@ public class ClientDetailsTasks {
|
||||
return start -> start.then(harness.tasks().ufo().operations().searchClient().byRc(rcOfExistingClient))
|
||||
.clickContactForwarding();
|
||||
}
|
||||
|
||||
public Function<STS701_S002_ClientDetailPage, STS701_S002_ClientDetailPage> assertFirstRequirementSolveDate() {
|
||||
return start -> start.menuClickClientRequirements()
|
||||
.clickRefreshRequirements()
|
||||
.assertFirstRequirementSolveDate(start.getActualDate());
|
||||
}
|
||||
|
||||
public Function<STS701_S002_ClientDetailPage, STS701_S002_ClientDetailPage> assertFirstRequirementSolveTime() {
|
||||
return start -> {
|
||||
start.menuClickClientRequirements()
|
||||
.clickRefreshRequirements();
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm:ss");
|
||||
LocalTime statusBarTime = LocalTime.parse(start.getStatusBarContent().split(" ")[0], formatter);
|
||||
LocalTime requirementSolveTime = LocalTime.parse(start.getFirstRequirementSolveDate().split(" ")[1], formatter);
|
||||
Assertions.assertTrue(Duration.between(statusBarTime, requirementSolveTime).getSeconds() <= 1,
|
||||
"Time difference between status bar time and requirement solve time is greater than 1 second");
|
||||
return start;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,9 +32,9 @@ public class CurrentAccountTasks {
|
||||
.clickSendButton()
|
||||
.onTransactionsPage()
|
||||
.clickBack()
|
||||
.clickDemand()
|
||||
.clickRefreshRequirementButton()
|
||||
.checkDemandDescriptionFirstRow("UFO - Bězný účet - Výpisy - Zaslání přes KC")
|
||||
.menuClickClientRequirements()
|
||||
.clickRefreshRequirements()
|
||||
.assertFirstRequirementDescription("UFO - Bězný účet - Výpisy - Zaslání přes KC")
|
||||
.clickRefreshClientButton();
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,9 +30,7 @@ public class DebitCardChangesTasks {
|
||||
.clickInternetTransactionsOn()
|
||||
.clickSaveSettings()
|
||||
.checkPaymentsThroughMotoInternet("Zapnuto - vše")
|
||||
.clickDemand()
|
||||
.clickRefreshRequirementButton()
|
||||
.checkDateInRequirement();
|
||||
.then(harness.tasks().ufo().operations().clientDetails().assertFirstRequirementSolveDate());
|
||||
}
|
||||
|
||||
public Function<STS701_S002_ClientDetailPage, STS701_S002_ClientDetailPage> stepsToTurnOffInternetPayments() {
|
||||
@ -41,9 +39,7 @@ public class DebitCardChangesTasks {
|
||||
.clickInternetTransactionsOff()
|
||||
.clickSaveSettings()
|
||||
.checkPaymentsThroughMotoInternet("Vypnuto - vše")
|
||||
.clickDemand()
|
||||
.clickRefreshRequirementButton()
|
||||
.checkDateInRequirement();
|
||||
.then(harness.tasks().ufo().operations().clientDetails().assertFirstRequirementSolveDate());
|
||||
}
|
||||
|
||||
public Function<PLK708_S001_DataChangesPage, STS701_S002_ClientDetailPage> setSendingAddressAndCheckChange(Address address, String postOffice, String requirementFirstRowText) {
|
||||
@ -56,9 +52,7 @@ public class DebitCardChangesTasks {
|
||||
.checkPostOfficeInput(postOffice)
|
||||
.clickSaveButton()
|
||||
.checkDebitCardSendingAddressStreetLabel(address.getStreet() + " " + address.getDescriptiveNumber())
|
||||
.clickDemand()
|
||||
.clickRefreshRequirementButton()
|
||||
.checkDateInRequirement()
|
||||
.checkDemandDescriptionFirstRow(requirementFirstRowText);
|
||||
.then(harness.tasks().ufo().operations().clientDetails().assertFirstRequirementSolveDate())
|
||||
.assertFirstRequirementDescription(requirementFirstRowText);
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ public class OperationsTasks {
|
||||
return new SavingAccountTasks(harness);
|
||||
}
|
||||
|
||||
public GdprTasks GdprTasks() {
|
||||
public GdprTasks gdprTasks() {
|
||||
return new GdprTasks(harness);
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ public class PaymentOrderTasks {
|
||||
.fillRecipientAccountNumber(accountNumberRecipient)
|
||||
.fillAmount(amount)
|
||||
.clickSend()
|
||||
.checkTransactionSuccessfullyEstablished("Transakce byla úspěšně založena pod ID:")
|
||||
.assertStatusBarContent("Transakce byla úspěšně založena pod ID:")
|
||||
.saveTaskIDPaymentOrder()
|
||||
.clickHomePage();
|
||||
}
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
package cz.moneta.test.dsl.util.task.ufo.operations;
|
||||
|
||||
import cz.moneta.test.dsl.Harness;
|
||||
import cz.moneta.test.dsl.ufo.operations.client.clientDetail.STS701_S002_ClientDetailPage;
|
||||
import cz.moneta.test.dsl.ufo.operations.search.OPS001_S001_ClientSearchPageUfoOperations;
|
||||
import cz.moneta.test.dsl.ufo.operations.search.SearchBy;
|
||||
import cz.moneta.test.dsl.ufo.operations.ufoOperations.*;
|
||||
import cz.moneta.test.dsl.util.DateUtils;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
import static cz.moneta.test.dsl.ufo.operations.ufoOperations.PSP701_S004_PrintTemplatePage.ATTACHMENT_PATH;
|
||||
import static cz.moneta.test.dsl.ufo.operations.ufoOperations.UKO720_S001_TaskList.TASK_ID_KEY;
|
||||
import static cz.moneta.test.dsl.ufo.operations.ufoOperations.UKO720_S001_TaskPage.ID_OBJECT_IN_TASK_KEY;
|
||||
import static cz.moneta.test.dsl.ufo.operations.ufoOperations.taskslist.TypesOfViewFilter.*;
|
||||
@ -97,7 +93,7 @@ public class TasksTasks {
|
||||
public Function<UKO720_S001_TaskPage, UKO720_S001_TaskPage> checkClientDetailInfo(String rcOfExistingClient) {
|
||||
return start -> start
|
||||
.clickClientDetailInfo()
|
||||
.checkClientRc(rcOfExistingClient)
|
||||
.assertClientRc(rcOfExistingClient)
|
||||
.checkPageNotContainsMenuElements()
|
||||
.closeSecondWindow();
|
||||
}
|
||||
|
||||
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