diff --git a/application/pom.xml b/application/pom.xml
index 61fa75cf49..4d89099d5b 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
application
diff --git a/application/src/main/data/upgrade/3.4.1/schema_update.sql b/application/src/main/data/upgrade/3.4.1/schema_update.sql
index 45a75c6bd9..2b7a4bdf26 100644
--- a/application/src/main/data/upgrade/3.4.1/schema_update.sql
+++ b/application/src/main/data/upgrade/3.4.1/schema_update.sql
@@ -88,7 +88,7 @@ $$
-- in case of running the upgrade script a second time:
IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN
ALTER TABLE edge_event RENAME TO old_edge_event;
- CREATE INDEX IF NOT EXISTS idx_old_blob_entity_created_time_tmp ON old_blob_entity(created_time);
+ CREATE INDEX IF NOT EXISTS idx_old_edge_event_created_time_tmp ON old_edge_event(created_time);
ALTER INDEX IF EXISTS idx_edge_event_tenant_id_and_created_time RENAME TO idx_old_edge_event_tenant_id_and_created_time;
FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts
diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
index 2deef1e13e..0aafe5e368 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
@@ -1436,7 +1436,8 @@ public class ControllerConstants {
protected static final String ATTRIBUTES_SCOPE_DESCRIPTION = "A string value representing the attributes scope. For example, 'SERVER_SCOPE'.";
protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'.";
- protected static final String ATTRIBUTES_SCOPE_ALLOWED_VALUES = "SERVER_SCOPE, CLIENT_SCOPE, SHARED_SCOPE";
+ protected static final String ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES = "SERVER_SCOPE, SHARED_SCOPE";
+ protected static final String ATTRIBUTES_SCOPE_ALLOWED_VALUES = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES + ", CLIENT_SCOPE";
protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details.";
protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
@@ -1462,10 +1463,12 @@ public class ControllerConstants {
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
- protected static final String ENTITY_ATTRIBUTE_SCOPES = " List of possible attribute scopes depends on the entity type: " +
+ protected static final String ENTITY_ATTRIBUTE_SCOPES_TEMPLATE = " List of possible attribute scopes depends on the entity type: " +
"\n\n * SERVER_SCOPE - supported for all entity types;" +
- "\n * CLIENT_SCOPE - supported for devices;" +
- "\n * SHARED_SCOPE - supported for devices. "+ "\n\n";
+ "\n * SHARED_SCOPE - supported for devices";
+ protected static final String ENTITY_SAVE_ATTRIBUTE_SCOPES = ENTITY_ATTRIBUTE_SCOPES_TEMPLATE + ".\n\n";
+ protected static final String ENTITY_GET_ATTRIBUTE_SCOPES = ENTITY_ATTRIBUTE_SCOPES_TEMPLATE +
+ ";\n * CLIENT_SCOPE - supported for devices. " + "\n\n";
protected static final String ATTRIBUTE_DATA_EXAMPLE = "[\n" +
" {\"key\": \"stringAttributeKey\", \"value\": \"value\", \"lastUpdateTs\": 1609459200000},\n" +
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 6f6c4dda85..f843e891be 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -58,7 +58,6 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.Aggregation;
-import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
@@ -92,11 +91,9 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -104,13 +101,15 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_JSON_REQUEST_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_KEYS_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_ALLOWED_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTE_DATA_EXAMPLE;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ATTRIBUTE_SCOPES;
+import static org.thingsboard.server.controller.ControllerConstants.ENTITY_GET_ATTRIBUTE_SCOPES;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION;
+import static org.thingsboard.server.controller.ControllerConstants.ENTITY_SAVE_ATTRIBUTE_SCOPES;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.INVALID_STRUCTURE_OF_THE_REQUEST;
@@ -241,7 +240,7 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Get attributes by scope (getAttributesByScope)",
notes = "Returns all attributes of a specified scope that belong to specified entity." +
- ENTITY_ATTRIBUTE_SCOPES +
+ ENTITY_GET_ATTRIBUTE_SCOPES +
"Use optional 'keys' parameter to return specific attributes."
+ "\n Example of the result: \n\n"
+ MARKDOWN_CODE_BLOCK_START
@@ -383,7 +382,7 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult saveDeviceAttributes(
@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
+ @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
@ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
try {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
@@ -395,7 +394,7 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Save entity attributes (saveEntityAttributesV1)",
notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " +
- ENTITY_ATTRIBUTE_SCOPES +
+ ENTITY_SAVE_ATTRIBUTE_SCOPES +
SAVE_ATTRIBUTES_REQUEST_PAYLOAD
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
@@ -411,7 +410,7 @@ public class TelemetryController extends BaseController {
public DeferredResult saveEntityAttributesV1(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
+ @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
@ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
try {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
@@ -423,7 +422,7 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Save entity attributes (saveEntityAttributesV2)",
notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " +
- ENTITY_ATTRIBUTE_SCOPES +
+ ENTITY_SAVE_ATTRIBUTE_SCOPES +
SAVE_ATTRIBUTES_REQUEST_PAYLOAD
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
@@ -439,7 +438,7 @@ public class TelemetryController extends BaseController {
public DeferredResult saveEntityAttributesV2(
@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
+ @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
@ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
try {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
index 8d2a8adc51..91f453d3de 100644
--- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
+++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/RuleChainsEdgeEventFetcher.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.service.edge.rpc.fetch;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
@@ -28,6 +30,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
+import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService.EDGE_IS_ROOT_BODY_KEY;
+
@Slf4j
@AllArgsConstructor
public class RuleChainsEdgeEventFetcher extends BasePageableEdgeEventFetcher {
@@ -41,7 +45,13 @@ public class RuleChainsEdgeEventFetcher extends BasePageableEdgeEventFetcher ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class);
+ assertThat(ruleChainUpdateMsgs).as("one msg during sync process, another from edge creation").hasSize(2);
assertThat(edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class)).as("one msg during sync process for 'default' device profile").hasSize(3);
assertThat(edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class)).as("one msg once device assigned to edge").hasSize(2);
assertThat(edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class)).as("two msgs during sync process for 'default' and 'test' asset profiles").hasSize(4);
assertThat(edgeImitator.findAllMessagesByType(AssetUpdateMsg.class)).as("two msgs - one during sync process, and one more once asset assigned to edge").hasSize(2);
assertThat(edgeImitator.findAllMessagesByType(UserUpdateMsg.class)).as("one msg during sync process for tenant admin user").hasSize(1);
assertThat(edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class)).as("admin setting update").hasSize(4);
+ verifyRuleChainMsgsAreRoot(ruleChainUpdateMsgs);
edgeImitator.expectMessageAmount(14);
doPost("/api/edge/sync/" + edge.getId());
assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue();
assertThat(edgeImitator.findAllMessagesByType(QueueUpdateMsg.class)).as("queue msg").hasSize(1);
- assertThat(edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class)).as("rule chain msg").hasSize(1);
+ ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class);
+ assertThat(ruleChainUpdateMsgs).as("rule chain msg").hasSize(1);
assertThat(edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class)).as("device profile msg").hasSize(2);
assertThat(edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class)).as("asset profile msg").hasSize(3);
assertThat(edgeImitator.findAllMessagesByType(AssetUpdateMsg.class)).as("asset update msg").hasSize(1);
assertThat(edgeImitator.findAllMessagesByType(UserUpdateMsg.class)).as("user update msg").hasSize(1);
assertThat(edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class)).as("admin setting update msg").hasSize(4);
assertThat(edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class)).as("asset update msg").hasSize(1);
+ verifyRuleChainMsgsAreRoot(ruleChainUpdateMsgs);
edgeImitator.allowIgnoredTypes();
try {
@@ -860,6 +864,12 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
+ private void verifyRuleChainMsgsAreRoot(List ruleChainUpdateMsgs) {
+ for (RuleChainUpdateMsg ruleChainUpdateMsg : ruleChainUpdateMsgs) {
+ Assert.assertTrue(ruleChainUpdateMsg.getRoot());
+ }
+ }
+
@Test
public void testDeleteEdgeWithDeleteRelationsOk() throws Exception {
EdgeId edgeId = savedEdge("Edge for Test WithRelationsOk").getId();
diff --git a/common/actor/pom.xml b/common/actor/pom.xml
index 8f2c1e1b13..baab985d07 100644
--- a/common/actor/pom.xml
+++ b/common/actor/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/cache/pom.xml b/common/cache/pom.xml
index e48d232b16..8c7b3231c2 100644
--- a/common/cache/pom.xml
+++ b/common/cache/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml
index a8dfc522ce..74a89a4c8b 100644
--- a/common/cluster-api/pom.xml
+++ b/common/cluster-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml
index 57bb96d2c2..7d82d8344a 100644
--- a/common/coap-server/pom.xml
+++ b/common/coap-server/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml
index cba811343a..749e704170 100644
--- a/common/dao-api/pom.xml
+++ b/common/dao-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/data/pom.xml b/common/data/pom.xml
index 9a95c08b65..4d18c99cc6 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml
index e09de1135f..71fc82184f 100644
--- a/common/edge-api/pom.xml
+++ b/common/edge-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/message/pom.xml b/common/message/pom.xml
index 5176e4af1a..b52053252b 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/pom.xml b/common/pom.xml
index c7b6d86eaf..20847853d7 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
common
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index 8485c2d63d..f4a72cc427 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/script/pom.xml b/common/script/pom.xml
index a849cf2684..7b22c4e0b0 100644
--- a/common/script/pom.xml
+++ b/common/script/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml
index 651eb153ed..cc2c129c3f 100644
--- a/common/script/remote-js-client/pom.xml
+++ b/common/script/remote-js-client/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
script
org.thingsboard.common.script
diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml
index 01fef3059b..08b6490376 100644
--- a/common/script/script-api/pom.xml
+++ b/common/script/script-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
script
org.thingsboard.common.script
diff --git a/common/stats/pom.xml b/common/stats/pom.xml
index 2fcac6e059..f20e313725 100644
--- a/common/stats/pom.xml
+++ b/common/stats/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
index f4089d9a79..20665d1708 100644
--- a/common/transport/coap/pom.xml
+++ b/common/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
index c6c8f9869d..4da59fc93f 100644
--- a/common/transport/http/pom.xml
+++ b/common/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml
index 3c6fae874f..724b140d87 100644
--- a/common/transport/lwm2m/pom.xml
+++ b/common/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
index 9442a66646..fa01fb61ad 100644
--- a/common/transport/mqtt/pom.xml
+++ b/common/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index 47432f47f2..8c017fa757 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index 939eecb009..fcba445de8 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
index 06fbfc5771..1221d42bcf 100644
--- a/common/transport/transport-api/pom.xml
+++ b/common/transport/transport-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.common.transport
diff --git a/common/util/pom.xml b/common/util/pom.xml
index 1a178b4c1e..b38f26abd1 100644
--- a/common/util/pom.xml
+++ b/common/util/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml
index 6ff68da760..0843494c49 100644
--- a/common/version-control/pom.xml
+++ b/common/version-control/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
common
org.thingsboard.common
diff --git a/dao/pom.xml b/dao/pom.xml
index 5335492759..563fc6b1e2 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
dao
diff --git a/msa/black-box-tests/README.md b/msa/black-box-tests/README.md
index a60e7405a8..ae0e8b79a0 100644
--- a/msa/black-box-tests/README.md
+++ b/msa/black-box-tests/README.md
@@ -30,9 +30,18 @@ As result, in REPOSITORY column, next images should be present:
mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.hybridMode=true
-To run the black box tests with using local env run tests in the [msa/black-box-tests](../black-box-tests) directory with runLocal property:
+- To run the black box tests with using local env run tests in the [msa/black-box-tests](../black-box-tests) directory with runLocal property:
mvn clean install -DblackBoxTests.skip=false -DrunLocal=true
+- To run ui smoke tests in the [msa/black-box-tests](../black-box-tests) directory specifying suite name:
+
+ mvn clean install -DblackBoxTests.skip=false -Dsuite=uiTests
+
+- To run all tests in the [msa/black-box-tests](../black-box-tests) directory specifying suite name:
+
+ mvn clean install -DblackBoxTests.skip=false -Dsuite=all
+
+
diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml
index 74c97fc87d..665650df89 100644
--- a/msa/black-box-tests/pom.xml
+++ b/msa/black-box-tests/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
@@ -34,6 +34,7 @@
UTF-8
${basedir}/../..
+ connectivity
@@ -170,6 +171,21 @@
${project.version}
test
+
+ org.seleniumhq.selenium
+ selenium-java
+ test
+
+
+ io.github.bonigarcia
+ webdrivermanager
+ test
+
+
+ io.qameta.allure
+ allure-testng
+ test
+
@@ -178,7 +194,7 @@
maven-surefire-plugin
- src/test/resources/testNG.xml
+ src/test/resources/${suite}.xml
${blackBoxTests.skip}
@@ -190,6 +206,16 @@
+
+ io.qameta.allure
+ allure-maven
+ ${allure-maven.version}
+
+ ${allure-testng.version}
+ https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/
+ ${allure-testng.version}/allure-commandline-${allure-testng.version}.zip
+
+
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
index d259d46b43..d0d66ab102 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
@@ -63,7 +63,7 @@ public abstract class AbstractContainerTest {
}
}
- @AfterSuite
+ @AfterSuite()
public void afterSuite() {
if (containerTestSuite.isActive()) {
containerTestSuite.stop();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
index 2ff7088fa0..5d10d06a90 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
@@ -109,7 +109,8 @@ public class ContainerTestSuite {
new File(targetDir + "docker-compose.postgres.volumes.yml"),
new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"),
new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")),
- new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml"))
+ new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml")),
+ new File(targetDir + ("docker-selenium.yml"))
));
Map queueEnv = new HashMap<>();
@@ -230,4 +231,8 @@ public class ContainerTestSuite {
fail("failed to update file");
}
}
+
+ public DockerComposeContainer> getTestContainer() {
+ return testContainer;
+ }
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/DisableUIListeners.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/DisableUIListeners.java
new file mode 100644
index 0000000000..86e9646516
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/DisableUIListeners.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface DisableUIListeners {
+
+}
+
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/SeleniumRemoteWebDriverTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/SeleniumRemoteWebDriverTest.java
new file mode 100644
index 0000000000..8c6f044f71
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/SeleniumRemoteWebDriverTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa;
+
+import com.google.common.io.Files;
+import io.qameta.allure.Attachment;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.remote.LocalFileDetector;
+import org.openqa.selenium.remote.RemoteWebDriver;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+@Slf4j
+public class SeleniumRemoteWebDriverTest {
+
+ static final int WIDTH = 1680;
+ static final int HEIGHT = 1050;
+ final Dimension dimension = new Dimension(WIDTH, HEIGHT);
+ WebDriver driver;
+
+ @SneakyThrows
+ @Attachment(value = "Page screenshot", type = "image/png")
+ public static byte[] captureScreen(WebDriver driver, String dirPath) {
+ File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
+ FileUtils.copyFile(screenshot, new File("./target/allure-results/screenshots/" + dirPath + "//" + screenshot.getName()));
+ return Files.toByteArray(screenshot);
+ }
+
+
+ /**
+ * Requirement:
+ * docker run --name=chrome --rm --network=host -p 4444:4444 -p 7900:7900 --shm-size="2g" -e SE_NODE_MAX_SESSIONS=8 -e SE_NODE_OVERRIDE_MAX_SESSIONS=true -e SE_NODE_SESSION_TIMEOUT=90 -e SE_SCREEN_WIDTH=1920 -e SE_SCREEN_HEIGHT=1080 -e SE_SCREEN_DEPTH=24 -e SE_SCREEN_DPI=74 selenium/standalone-chrome
+ * */
+ @BeforeEach
+ void setUp() throws MalformedURLException {
+ log.info("Requirement:");
+ log.info("docker run --name=chrome --rm --network=host -p 4444:4444 -p 7900:7900 --shm-size=\"2g\" -e SE_NODE_MAX_SESSIONS=8 -e SE_NODE_OVERRIDE_MAX_SESSIONS=true -e SE_NODE_SESSION_TIMEOUT=90 -e SE_SCREEN_WIDTH=1920 -e SE_SCREEN_HEIGHT=1080 -e SE_SCREEN_DEPTH=24 -e SE_SCREEN_DPI=74 selenium/standalone-chrome");
+ log.info("*----------------------* Setup driver *----------------------*");
+ ChromeOptions options = new ChromeOptions();
+ RemoteWebDriver remoteWebDriver = new RemoteWebDriver(new URL("http://127.0.0.1:4444"), options);
+ remoteWebDriver.setFileDetector(new LocalFileDetector());
+ driver = remoteWebDriver;
+ driver.manage().window().setSize(dimension);
+ }
+
+ @AfterEach
+ void tearDown() {
+ log.info("*----------------------* Teardown *----------------------*");
+ driver.quit();
+ }
+
+ @Test
+ void testSeleniumConnection() {
+ driver.get("https://thingsboard.io/");
+ captureScreen(driver, "success");
+ log.info("Check the screenshot on target/allure-results/screenshots/success/screenshot???????????????.png");
+ //Thread.sleep(TimeUnit.SECONDS.toMillis(30));
+ }
+
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java
index 51bc75c86a..ec23b69812 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java
@@ -16,18 +16,22 @@
package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j;
-import org.testng.ITestContext;
+import org.openqa.selenium.WebDriver;
+import org.testng.ITestListener;
import org.testng.ITestResult;
-import org.testng.TestListenerAdapter;
+import org.testng.internal.ConstructorOrMethod;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
import static org.testng.internal.Utils.log;
+import static org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest.captureScreen;
@Slf4j
-public class TestListener extends TestListenerAdapter {
+public class TestListener implements ITestListener {
+
+ WebDriver driver;
@Override
public void onTestStart(ITestResult result) {
- super.onTestStart(result);
log.info("===>>> Test started: " + result.getName());
}
@@ -36,10 +40,14 @@ public class TestListener extends TestListenerAdapter {
*/
@Override
public void onTestSuccess(ITestResult result) {
- super.onTestSuccess(result);
- if (result != null) {
- log.info("<<<=== Test completed successfully: " + result.getName());
+ log.info("<<<=== Test completed successfully: " + result.getName());
+ ConstructorOrMethod consOrMethod = result.getMethod().getConstructorOrMethod();
+ DisableUIListeners disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableUIListeners.class);
+ if (disable != null) {
+ return;
}
+ driver = ((AbstractDriverBaseTest) result.getInstance()).getDriver();
+ captureScreen(driver, "success");
}
/**
@@ -47,7 +55,28 @@ public class TestListener extends TestListenerAdapter {
*/
@Override
public void onTestFailure(ITestResult result) {
- super.onTestFailure(result);
log.info("<<<=== Test failed: " + result.getName());
+ ConstructorOrMethod consOrMethod = result.getMethod().getConstructorOrMethod();
+ DisableUIListeners disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableUIListeners.class);
+ if (disable != null) {
+ return;
+ }
+ driver = ((AbstractDriverBaseTest) result.getInstance()).getDriver();
+ captureScreen(driver, "failure");
+ }
+
+ /**
+ * Invoked when a test skipped
+ */
+ @Override
+ public void onTestSkipped(ITestResult result) {
+ log.info("<<<=== Test skipped: " + result.getName());
+ ConstructorOrMethod consOrMethod = result.getMethod().getConstructorOrMethod();
+ DisableUIListeners disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableUIListeners.class);
+ if (disable != null) {
+ return;
+ }
+ driver = ((AbstractDriverBaseTest) result.getInstance()).getDriver();
+ captureScreen(driver, "skipped");
}
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java
index 020dbf8b93..b5d91647c1 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java
@@ -39,6 +39,13 @@ public class TestProperties {
return getProperties().getProperty("tb.baseUrl");
}
+ public static String getBaseUiUrl() {
+ if (instance.isActive()) {
+ return "https://host.docker.internal";
+ }
+ return getProperties().getProperty("tb.baseUrl");
+ }
+
public static String getWebSocketUrl() {
if (instance.isActive()) {
return WSS_URL;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java
index 138c74e9b6..46fed2c870 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java
@@ -26,8 +26,10 @@ import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
+import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
+import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
@@ -78,7 +80,7 @@ public class TestRestClient {
loginRequest.put("password", password);
JsonPath jsonPath = given().spec(requestSpec).body(loginRequest)
- .post( "/api/auth/login")
+ .post("/api/auth/login")
.getBody().jsonPath();
token = jsonPath.get("token");
refreshToken = jsonPath.get("refreshToken");
@@ -86,7 +88,7 @@ public class TestRestClient {
}
public Device postDevice(String accessToken, Device device) {
- return given().spec(requestSpec).body(device)
+ return given().spec(requestSpec).body(device)
.pathParams("accessToken", accessToken)
.post("/api/device?accessToken={accessToken}")
.then()
@@ -105,62 +107,65 @@ public class TestRestClient {
}
public ValidatableResponse getDeviceById(DeviceId deviceId, int statusCode) {
- return given().spec(requestSpec)
+ return given().spec(requestSpec)
.pathParams("deviceId", deviceId.getId())
.get("/api/device/{deviceId}")
.then()
.statusCode(statusCode);
}
+
public Device getDeviceById(DeviceId deviceId) {
- return getDeviceById(deviceId, HTTP_OK)
+ return getDeviceById(deviceId, HTTP_OK)
.extract()
.as(Device.class);
}
+
public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
return given().spec(requestSpec).get("/api/device/{deviceId}/credentials", deviceId.getId())
- .then()
- .assertThat()
- .statusCode(HTTP_OK)
- .extract()
- .as(DeviceCredentials.class);
+ .then()
+ .assertThat()
+ .statusCode(HTTP_OK)
+ .extract()
+ .as(DeviceCredentials.class);
}
public ValidatableResponse postTelemetry(String credentialsId, JsonNode telemetry) {
- return given().spec(requestSpec).body(telemetry)
- .post("/api/v1/{credentialsId}/telemetry", credentialsId)
- .then()
- .statusCode(HTTP_OK);
+ return given().spec(requestSpec).body(telemetry)
+ .post("/api/v1/{credentialsId}/telemetry", credentialsId)
+ .then()
+ .statusCode(HTTP_OK);
}
public ValidatableResponse deleteDevice(DeviceId deviceId) {
- return given().spec(requestSpec)
+ return given().spec(requestSpec)
.delete("/api/device/{deviceId}", deviceId.getId())
.then()
.statusCode(HTTP_OK);
}
+
public ValidatableResponse deleteDeviceIfExists(DeviceId deviceId) {
- return given().spec(requestSpec)
+ return given().spec(requestSpec)
.delete("/api/device/{deviceId}", deviceId.getId())
.then()
- .statusCode(anyOf(is(HTTP_OK),is(HTTP_NOT_FOUND)));
+ .statusCode(anyOf(is(HTTP_OK), is(HTTP_NOT_FOUND)));
}
public ValidatableResponse postTelemetryAttribute(String entityType, DeviceId deviceId, String scope, JsonNode attribute) {
- return given().spec(requestSpec).body(attribute)
+ return given().spec(requestSpec).body(attribute)
.post("/api/plugins/telemetry/{entityType}/{entityId}/attributes/{scope}", entityType, deviceId.getId(), scope)
.then()
.statusCode(HTTP_OK);
}
public ValidatableResponse postAttribute(String accessToken, JsonNode attribute) {
- return given().spec(requestSpec).body(attribute)
+ return given().spec(requestSpec).body(attribute)
.post("/api/v1/{accessToken}/attributes/", accessToken)
.then()
.statusCode(HTTP_OK);
}
public JsonNode getAttributes(String accessToken, String clientKeys, String sharedKeys) {
- return given().spec(requestSpec)
+ return given().spec(requestSpec)
.queryParam("clientKeys", clientKeys)
.queryParam("sharedKeys", sharedKeys)
.get("/api/v1/{accessToken}/attributes", accessToken)
@@ -186,10 +191,11 @@ public class TestRestClient {
.then()
.statusCode(HTTP_OK)
.extract()
- .as(new TypeRef>() {});
+ .as(new TypeRef>() {
+ });
}
- public RuleChain postRootRuleChain(RuleChain ruleChain) {
+ public RuleChain postRuleChain(RuleChain ruleChain) {
return given().spec(requestSpec)
.body(ruleChain)
.post("/api/ruleChain")
@@ -258,7 +264,8 @@ public class TestRestClient {
.then()
.statusCode(HTTP_OK)
.extract()
- .as(new TypeRef>() {});
+ .as(new TypeRef>() {
+ });
}
public JsonNode postServerSideRpc(DeviceId deviceId, JsonNode serverRpcPayload) {
@@ -289,6 +296,35 @@ public class TestRestClient {
.as(DeviceProfile.class);
}
+ public Customer postCustomer(Customer customer) {
+ return given().spec(requestSpec)
+ .body(customer)
+ .post("/api/customer")
+ .then()
+ .statusCode(HTTP_OK)
+ .extract()
+ .as(Customer.class);
+ }
+
+ public void deleteCustomer(CustomerId customerId) {
+ given().spec(requestSpec)
+ .delete("/api/customer/{customerId}", customerId.getId())
+ .then()
+ .statusCode(HTTP_OK);
+ }
+
+ public PageData getCustomers(PageLink pageLink) {
+ Map params = new HashMap<>();
+ addPageLinkToParam(params, pageLink);
+ return given().spec(requestSpec).queryParams(params)
+ .get("/api/customers")
+ .then()
+ .statusCode(HTTP_OK)
+ .extract()
+ .as(new TypeRef>() {
+ });
+ }
+
public String getToken() {
return token;
}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java
index fe3443dd0e..8ddc7e79c0 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/CoapClientTest.java
@@ -18,7 +18,6 @@ package org.thingsboard.server.msa.connectivity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject;
-import io.restassured.path.json.JsonPath;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -29,11 +28,13 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.DisableUIListeners;
import org.thingsboard.server.msa.TestCoapClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype;
+@DisableUIListeners
public class CoapClientTest extends AbstractContainerTest {
private TestCoapClient client;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
index adaefa887a..36da84e9d2 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
@@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.DisableUIListeners;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
@@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype;
+@DisableUIListeners
public class HttpClientTest extends AbstractContainerTest {
private Device device;
@BeforeMethod
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java
index 347598a9e7..7899372566 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.DisableUIListeners;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.mapper.AttributesResponse;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
@@ -68,6 +69,7 @@ import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype;
+@DisableUIListeners
@Slf4j
public class MqttClientTest extends AbstractContainerTest {
@@ -426,7 +428,7 @@ public class MqttClientTest extends AbstractContainerTest {
RuleChain newRuleChain = new RuleChain();
newRuleChain.setName("testRuleChain");
- RuleChain ruleChain = testRestClient.postRootRuleChain(newRuleChain);
+ RuleChain ruleChain = testRestClient.postRuleChain(newRuleChain);
JsonNode configuration = mapper.readTree(this.getClass().getClassLoader().getResourceAsStream("RpcResponseRuleChainMetadata.json"));
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
index 84b2dd7253..6315cad5b0 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.DisableUIListeners;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
@@ -66,6 +67,7 @@ import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultGatewayPrototype;
+@DisableUIListeners
@Slf4j
public class MqttGatewayClientTest extends AbstractContainerTest {
private Device gatewayDevice;
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractBasePage.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractBasePage.java
new file mode 100644
index 0000000000..9e32d6e917
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractBasePage.java
@@ -0,0 +1,143 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.base;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebDriverException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+@Slf4j
+abstract public class AbstractBasePage {
+ protected WebDriver driver;
+ protected WebDriverWait wait;
+ protected Actions actions;
+
+ public AbstractBasePage(WebDriver driver) {
+ this.driver = driver;
+ this.wait = new WebDriverWait(driver, Duration.ofMillis(5000));
+ this.actions = new Actions(driver);
+ }
+
+ @SneakyThrows
+ protected static void sleep(double second) {
+ Thread.sleep((long) (second * 1000L));
+ }
+
+ protected WebElement waitUntilVisibilityOfElementLocated(String locator) {
+ try {
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(locator)));
+ } catch (WebDriverException e) {
+ log.error("No visibility element: " + locator);
+ return null;
+ }
+ }
+
+ protected WebElement waitUntilElementToBeClickable(String locator) {
+ try {
+ return wait.until(ExpectedConditions.elementToBeClickable(By.xpath(locator)));
+ } catch (WebDriverException e) {
+ log.error("No clickable element: " + locator);
+ return null;
+ }
+ }
+
+ protected List waitUntilVisibilityOfElementsLocated(String locator) {
+ try {
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(locator)));
+ return driver.findElements(By.xpath(locator));
+ } catch (WebDriverException e) {
+ log.error("No visibility elements: " + locator);
+ return null;
+ }
+ }
+
+ protected List waitUntilElementsToBeClickable(String locator) {
+ try {
+ wait.until(ExpectedConditions.elementToBeClickable(By.xpath(locator)));
+ return driver.findElements(By.xpath(locator));
+ } catch (WebDriverException e) {
+ log.error("No clickable elements: " + locator);
+ return null;
+ }
+ }
+
+ public void waitUntilUrlContainsText(String urlPath) {
+ try {
+ wait.until(ExpectedConditions.urlContains(urlPath));
+ } catch (WebDriverException e) {
+ log.error("This URL path is missing");
+ }
+ }
+
+ protected void moveCursor(WebElement element) {
+ actions.moveToElement(element).perform();
+ }
+
+ protected void doubleClick(WebElement element) {
+ actions.doubleClick(element).build().perform();
+ }
+
+ public boolean elementIsNotPresent(String locator) {
+ try {
+ return wait.until(ExpectedConditions.not(ExpectedConditions.visibilityOfElementLocated(By.xpath(locator))));
+ } catch (WebDriverException e) {
+ throw new AssertionError("Element is present");
+ }
+ }
+
+ public boolean elementsIsNotPresent(String locator) {
+ try {
+ return wait.until(ExpectedConditions.not(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath(locator))));
+ } catch (WebDriverException e) {
+ throw new AssertionError("Elements is present");
+ }
+ }
+
+ public void waitUntilNumberOfTabToBe(int tabNumber) {
+ try {
+ wait.until(ExpectedConditions.numberOfWindowsToBe(tabNumber));
+ } catch (WebDriverException e) {
+ log.error("No tabs with this number");
+ }
+ }
+
+ public void goToNextTab(int tabNumber) {
+ waitUntilNumberOfTabToBe(tabNumber);
+ ArrayList tabs = new ArrayList<>(driver.getWindowHandles());
+ driver.switchTo().window(tabs.get(tabNumber - 1));
+ }
+
+ public static long getRandomNumber() {
+ return System.currentTimeMillis();
+ }
+
+ public static char getRandomSymbol() {
+ Random rand = new Random();
+ String s = "~`!@#$^&*()_+=-";
+ return s.charAt(rand.nextInt(s.length()));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java
new file mode 100644
index 0000000000..121acb4e92
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/base/AbstractDriverBaseTest.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.base;
+
+import com.google.common.io.Files;
+import io.github.bonigarcia.wdm.WebDriverManager;
+import io.qameta.allure.Attachment;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebDriverException;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+import org.openqa.selenium.remote.LocalFileDetector;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.msa.AbstractContainerTest;
+import org.thingsboard.server.msa.ContainerTestSuite;
+
+import java.io.File;
+import java.net.URL;
+import java.time.Duration;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.server.msa.TestProperties.getBaseUiUrl;
+
+@Slf4j
+abstract public class AbstractDriverBaseTest extends AbstractContainerTest {
+
+ protected WebDriver driver;
+ private final Dimension dimension = new Dimension(WIDTH, HEIGHT);
+ private static final int WIDTH = 1680;
+ private static final int HEIGHT = 1050;
+ private static final String REMOTE_WEBDRIVER_HOST = "http://localhost:4444";
+ protected static final PageLink pageLink = new PageLink(10);
+ private static final ContainerTestSuite instance = ContainerTestSuite.getInstance();
+
+ @SneakyThrows
+ @BeforeMethod
+ public void openBrowser() {
+ log.info("===>>> Setup driver");
+ ChromeOptions options = new ChromeOptions();
+ options.setAcceptInsecureCerts(true);
+ if (instance.isActive()) {
+ RemoteWebDriver remoteWebDriver = new RemoteWebDriver(new URL(REMOTE_WEBDRIVER_HOST), options);
+ remoteWebDriver.setFileDetector(new LocalFileDetector());
+ driver = remoteWebDriver;
+ } else {
+ WebDriverManager.chromedriver().setup();
+ driver = new ChromeDriver(options);
+ }
+ driver.manage().window().setSize(dimension);
+ }
+
+ @AfterMethod
+ public void closeBrowser() {
+ log.info("<<<=== Teardown");
+ driver.quit();
+ }
+
+ public void openLocalhost() {
+ driver.get(getBaseUiUrl());
+ }
+
+ public String getUrl() {
+ return driver.getCurrentUrl();
+ }
+
+ public WebDriver getDriver() {
+ return driver;
+ }
+
+ protected boolean urlContains(String urlPath) {
+ WebDriverWait wait = new WebDriverWait(driver, Duration.ofMillis(5000));
+ try {
+ wait.until(ExpectedConditions.urlContains(urlPath));
+ } catch (WebDriverException e) {
+ log.error("This URL path is missing");
+ }
+ return driver.getCurrentUrl().contains(urlPath);
+ }
+
+ public static RuleChain getRuleChainByName(String name) {
+ return testRestClient.getRuleChains(pageLink).getData().stream()
+ .filter(s -> s.getName().equals(name)).collect(Collectors.toList()).get(0);
+ }
+
+ public static Customer getCustomerByName(String name) {
+ return testRestClient.getCustomers(pageLink).getData().stream()
+ .filter(x -> x.getName().equals(name)).collect(Collectors.toList()).get(0);
+ }
+
+ @SneakyThrows
+ @Attachment(value = "Page screenshot", type = "image/png")
+ public static byte[] captureScreen(WebDriver driver, String dirPath) {
+ File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
+ FileUtils.copyFile(screenshot, new File("./target/allure-results/screenshots/" + dirPath + "//" + screenshot.getName()));
+ return Files.toByteArray(screenshot);
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryAnalyzer.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryAnalyzer.java
new file mode 100644
index 0000000000..56cd7c91cd
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryAnalyzer.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.listeners;
+
+import org.testng.IRetryAnalyzer;
+import org.testng.ITestResult;
+import org.testng.internal.ConstructorOrMethod;
+import org.thingsboard.server.msa.DisableUIListeners;
+
+public class RetryAnalyzer implements IRetryAnalyzer {
+
+ private int retryCount = 0;
+ private static final int MAX_RETRY_COUNT = 2;
+
+ @Override
+ public boolean retry(ITestResult result) {
+ ConstructorOrMethod consOrMethod = result.getMethod().getConstructorOrMethod();
+ DisableUIListeners disable = consOrMethod.getMethod().getDeclaringClass().getAnnotation(DisableUIListeners.class);
+ if (disable != null) {
+ return false;
+ }
+ if (retryCount < MAX_RETRY_COUNT) {
+ System.out.printf("Retrying test %s for the %d time(s).%n", result.getName(), retryCount + 1);
+ retryCount++;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryTestListener.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryTestListener.java
new file mode 100644
index 0000000000..0411083f87
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/listeners/RetryTestListener.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.listeners;
+
+import org.testng.IAnnotationTransformer;
+import org.testng.annotations.ITestAnnotation;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class RetryTestListener implements IAnnotationTransformer {
+
+ @Override
+ public void transform(ITestAnnotation annotation,
+ Class testClass,
+ Constructor testConstructor,
+ Method testMethod) {
+ annotation.setRetryAnalyzer(RetryAnalyzer.class);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java
new file mode 100644
index 0000000000..05da22e4db
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageElements.java
@@ -0,0 +1,282 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.List;
+
+public class CustomerPageElements extends OtherPageElementsHelper {
+ public CustomerPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String CUSTOMER = "//mat-row//span[contains(text(),'%s')]";
+ private static final String EMAIL = ENTITY + "/../..//mat-cell[contains(@class,'email')]/span";
+ private static final String COUNTRY = ENTITY + "/../..//mat-cell[contains(@class,'country')]/span";
+ private static final String CITY = ENTITY + "/../..//mat-cell[contains(@class,'city')]/span";
+ private static final String TITLES = "//mat-cell[contains(@class,'cdk-column-title')]/span";
+ protected static final String EDIT_MENU_DASHBOARD_FIELD = "//input[@formcontrolname='dashboard']";
+ private static final String EDIT_MENU_DASHBOARD = "//div[@class='cdk-overlay-pane']//span/span";
+ private static final String MANAGE_CUSTOMERS_USERS_BTN = ENTITY + "/../..//mat-icon[contains(text(),' account_circle')]/../..";
+ private static final String MANAGE_CUSTOMERS_ASSETS_BTN = ENTITY + "/../..//mat-icon[contains(text(),' domain')]/../..";
+ private static final String MANAGE_CUSTOMERS_DEVICES_BTN = ENTITY + "/../..//mat-icon[contains(text(),' devices_other')]/../..";
+ private static final String MANAGE_CUSTOMERS_DASHBOARDS_BTN = ENTITY + "/../..//mat-icon[contains(text(),' dashboard')]/../..";
+ private static final String MANAGE_CUSTOMERS_EDGE_BTN = ENTITY + "/../..//mat-icon[contains(text(),' router')]/../..";
+ private static final String ADD_USER_EMAIL = "//tb-add-user-dialog//input[@formcontrolname='email']";
+ private static final String ACTIVATE_WINDOW_OK_BTN = "//span[contains(text(),'OK')]";
+ private static final String USER_LOGIN_BTN = "//mat-icon[@data-mat-icon-name='login']";
+ private static final String USERS_WIDGET = "//tb-widget";
+ private static final String SELECT_COUNTRY_MENU = "//mat-form-field//mat-select[@formcontrolname='country']";
+ private static final String COUNTRIES = "//span[@class='mat-option-text']";
+ protected static final String INPUT_FIELD = "//input[@formcontrolname='%s']";
+ protected static final String INPUT_FIELD_NAME_TITLE = "title";
+ private static final String INPUT_FIELD_NAME_CITY = "city";
+ private static final String INPUT_FIELD_NAME_STATE = "state";
+ private static final String INPUT_FIELD_NAME_ZIP = "zip";
+ private static final String INPUT_FIELD_NAME_ADDRESS = "address";
+ private static final String INPUT_FIELD_NAME_ADDRESS2 = "address2";
+ private static final String INPUT_FIELD_NAME_EMAIL = "email";
+ private static final String INPUT_FIELD_NAME_NUMBER = "phoneNumber";
+ private static final String INPUT_FIELD_NAME_ASSIGNED_LIST = "entity";
+ private static final String ASSIGNED_BTN = "//button[@type='submit']";
+ private static final String HIDE_HOME_DASHBOARD_TOOLBAR = "//mat-checkbox[@formcontrolname='homeDashboardHideToolbar']/label";
+ private static final String FILTER_BTN = "//tb-filters-edit";
+ private static final String TIME_BTN = "//tb-timewindow";
+ private static final String CUSTOMER_ICON_HEADER = "//tb-breadcrumb//span[contains(text(),'Customer %s')]";
+ private static final String CUSTOMER_USER_ICON_HEADER = "Users";
+ private static final String CUSTOMER_ASSETS_ICON_HEADER = "Assets";
+ private static final String CUSTOMER_DEVICES_ICON_HEADER = "Devices";
+ private static final String CUSTOMER_DASHBOARD_ICON_HEADER = "Dashboards";
+ private static final String CUSTOMER_EDGE_ICON_HEADER = "edge instances";
+ private static final String CUSTOMER_USER_ICON_HEAD = "(//mat-drawer-content//span[contains(@class,'tb-entity-table')])[1]";
+ private static final String MANAGE_BTN_VIEW = "//span[contains(text(),'%s')]";
+ private static final String MANAGE_CUSTOMERS_USERS_BTN_VIEW = "Manage users";
+ private static final String MANAGE_CUSTOMERS_ASSETS_BTN_VIEW = "Manage assets";
+ private static final String MANAGE_CUSTOMERS_DEVICE_BTN_VIEW = "Manage devices";
+ private static final String MANAGE_CUSTOMERS_DASHBOARD_BTN_VIEW = "Manage dashboards";
+ private static final String MANAGE_CUSTOMERS_EDGE_BTN_VIEW = "Manage edges ";
+ private static final String DELETE_FROM_VIEW_BTN = "//tb-customer//span[contains(text(),' Delete')]";
+
+ public WebElement titleFieldAddEntityView() {
+ return waitUntilElementToBeClickable(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_TITLE));
+ }
+
+ public WebElement titleFieldEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_TITLE));
+ }
+
+ public WebElement customer(String entityName) {
+ return waitUntilElementToBeClickable(String.format(CUSTOMER, entityName));
+ }
+
+ public WebElement email(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(EMAIL, entityName));
+ }
+
+ public WebElement country(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(COUNTRY, entityName));
+ }
+
+ public WebElement city(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(CITY, entityName));
+ }
+
+ public List entityTitles() {
+ return waitUntilVisibilityOfElementsLocated(TITLES);
+ }
+
+ public WebElement editMenuDashboardField() {
+ return waitUntilVisibilityOfElementLocated(EDIT_MENU_DASHBOARD_FIELD);
+ }
+
+ public WebElement editMenuDashboard() {
+ return waitUntilElementToBeClickable(EDIT_MENU_DASHBOARD);
+ }
+
+ public WebElement phoneNumberEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_NUMBER));
+ }
+
+ public WebElement phoneNumberAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_NUMBER));
+ }
+
+ public WebElement manageCustomersUserBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_CUSTOMERS_USERS_BTN, title));
+ }
+
+ public WebElement manageCustomersAssetsBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_CUSTOMERS_ASSETS_BTN, title));
+ }
+
+ public WebElement manageCustomersDevicesBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_CUSTOMERS_DEVICES_BTN, title));
+ }
+
+ public WebElement manageCustomersDashboardsBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_CUSTOMERS_DASHBOARDS_BTN, title));
+ }
+
+ public WebElement manageCustomersEdgeBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_CUSTOMERS_EDGE_BTN, title));
+ }
+
+ public WebElement addUserEmailField() {
+ return waitUntilElementToBeClickable(ADD_USER_EMAIL);
+ }
+
+ public WebElement activateWindowOkBtn() {
+ return waitUntilElementToBeClickable(ACTIVATE_WINDOW_OK_BTN);
+ }
+
+ public WebElement userLoginBtn() {
+ return waitUntilElementToBeClickable(USER_LOGIN_BTN);
+ }
+
+ public WebElement usersWidget() {
+ return waitUntilVisibilityOfElementLocated(USERS_WIDGET);
+ }
+
+ public WebElement countrySelectMenuEntityView() {
+ return waitUntilElementToBeClickable(SELECT_COUNTRY_MENU);
+ }
+
+ public WebElement countrySelectMenuAddEntityView() {
+ return waitUntilElementToBeClickable(ADD_ENTITY_VIEW + SELECT_COUNTRY_MENU);
+ }
+
+ public List countries() {
+ return waitUntilElementsToBeClickable(COUNTRIES);
+ }
+
+ public WebElement cityEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_CITY));
+ }
+
+ public WebElement cityAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_CITY));
+ }
+
+ public WebElement stateEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_STATE));
+ }
+
+ public WebElement stateAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_STATE));
+ }
+
+ public WebElement zipEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_ZIP));
+ }
+
+ public WebElement zipAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_ZIP));
+ }
+
+ public WebElement addressEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_ADDRESS));
+ }
+
+ public WebElement addressAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_ADDRESS));
+ }
+
+ public WebElement address2EntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_ADDRESS2));
+ }
+
+ public WebElement address2AddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_ADDRESS2));
+ }
+
+ public WebElement emailEntityView() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_EMAIL));
+ }
+
+ public WebElement emailAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW + String.format(INPUT_FIELD, INPUT_FIELD_NAME_EMAIL));
+ }
+
+ public WebElement assignedField() {
+ return waitUntilVisibilityOfElementLocated(String.format(INPUT_FIELD, INPUT_FIELD_NAME_ASSIGNED_LIST));
+ }
+
+ public WebElement submitAssignedBtn() {
+ return waitUntilElementToBeClickable(ASSIGNED_BTN);
+ }
+
+ public WebElement hideHomeDashboardToolbarCheckbox() {
+ return waitUntilElementToBeClickable(HIDE_HOME_DASHBOARD_TOOLBAR);
+ }
+
+ public WebElement filterBtn() {
+ return waitUntilVisibilityOfElementLocated(FILTER_BTN);
+ }
+
+ public WebElement timeBtn() {
+ return waitUntilVisibilityOfElementLocated(TIME_BTN);
+ }
+
+ public WebElement customerUserIconHeader() {
+ return waitUntilVisibilityOfElementLocated(String.format(CUSTOMER_ICON_HEADER, CUSTOMER_USER_ICON_HEADER));
+ }
+
+ public WebElement customerAssetsIconHeader() {
+ return waitUntilVisibilityOfElementLocated(String.format(CUSTOMER_ICON_HEADER, CUSTOMER_ASSETS_ICON_HEADER));
+ }
+
+ public WebElement customerDevicesIconHeader() {
+ return waitUntilVisibilityOfElementLocated(String.format(CUSTOMER_ICON_HEADER, CUSTOMER_DEVICES_ICON_HEADER));
+ }
+
+ public WebElement customerDashboardIconHeader() {
+ return waitUntilVisibilityOfElementLocated(String.format(CUSTOMER_ICON_HEADER, CUSTOMER_DASHBOARD_ICON_HEADER));
+ }
+
+ public WebElement customerEdgeIconHeader() {
+ return waitUntilVisibilityOfElementLocated(String.format(CUSTOMER_ICON_HEADER, CUSTOMER_EDGE_ICON_HEADER));
+ }
+
+ public WebElement customerManageWindowIconHead() {
+ return waitUntilVisibilityOfElementLocated(CUSTOMER_USER_ICON_HEAD);
+ }
+
+ public WebElement manageCustomersUserBtnView() {
+ return waitUntilElementToBeClickable(String.format(MANAGE_BTN_VIEW, MANAGE_CUSTOMERS_USERS_BTN_VIEW));
+ }
+
+ public WebElement manageCustomersAssetsBtnView() {
+ return waitUntilElementToBeClickable(String.format(MANAGE_BTN_VIEW, MANAGE_CUSTOMERS_ASSETS_BTN_VIEW));
+ }
+
+ public WebElement manageCustomersDeviceBtnView() {
+ return waitUntilElementToBeClickable(String.format(MANAGE_BTN_VIEW, MANAGE_CUSTOMERS_DEVICE_BTN_VIEW));
+ }
+
+ public WebElement manageCustomersDashboardsBtnView() {
+ return waitUntilElementToBeClickable(String.format(MANAGE_BTN_VIEW, MANAGE_CUSTOMERS_DASHBOARD_BTN_VIEW));
+ }
+
+ public WebElement manageCustomersEdgeBtnView() {
+ return waitUntilElementToBeClickable(String.format(MANAGE_BTN_VIEW, MANAGE_CUSTOMERS_EDGE_BTN_VIEW));
+ }
+
+ public WebElement customerViewDeleteBtn() {
+ return waitUntilElementToBeClickable(DELETE_FROM_VIEW_BTN);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java
new file mode 100644
index 0000000000..9956966a16
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/CustomerPageHelper.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import lombok.extern.slf4j.Slf4j;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+
+@Slf4j
+public class CustomerPageHelper extends CustomerPageElements {
+ public CustomerPageHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ private String customerName;
+ private String country;
+ private String dashboard;
+ private String dashboardFromView;
+
+ private String customerEmail;
+ private String customerCountry;
+ private String customerCity;
+
+ public void setCustomerName() {
+ this.customerName = entityTitles().get(0).getText();
+ }
+
+ public void setCustomerName(int number) {
+ this.customerName = entityTitles().get(number).getText();
+ }
+
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ public void setCountry() {
+ this.country = countries().get(0).getText();
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setDashboard() {
+ this.dashboard = listOfEntity().get(0).getText();
+ }
+
+ public void setDashboardFromView() {
+ this.dashboardFromView = editMenuDashboardField().getAttribute("value");
+ }
+
+ public String getDashboard() {
+ return dashboard;
+ }
+
+ public String getDashboardFromView() {
+ return dashboardFromView;
+ }
+
+ public void setCustomerEmail(String title) {
+ this.customerEmail = email(title).getText();
+ }
+
+ public String getCustomerEmail() {
+ return customerEmail;
+ }
+
+ public void setCustomerCountry(String title) {
+ this.customerCountry = country(title).getText();
+ }
+
+ public String getCustomerCountry() {
+ return customerCountry;
+ }
+
+ public void setCustomerCity(String title) {
+ this.customerCity = city(title).getText();
+ }
+
+ public String getCustomerCity() {
+ return customerCity;
+ }
+
+ public void changeTitleEditMenu(String newTitle) {
+ titleFieldEntityView().clear();
+ wait.until(ExpectedConditions.textToBe(By.xpath(String.format(INPUT_FIELD, INPUT_FIELD_NAME_TITLE)), ""));
+ titleFieldEntityView().sendKeys(newTitle);
+ }
+
+ public void chooseDashboard() {
+ editMenuDashboardField().click();
+ sleep(0.5);
+ editMenuDashboard().click();
+ sleep(0.5);
+ }
+
+ public void createCustomersUser() {
+ plusBtn().click();
+ addUserEmailField().sendKeys(getRandomNumber() + "@gmail.com");
+ addBtnC().click();
+ activateWindowOkBtn().click();
+ }
+
+ public void selectCountryEntityView() {
+ countrySelectMenuEntityView().click();
+ setCountry();
+ countries().get(0).click();
+ }
+
+ public void selectCountryAddEntityView() {
+ countrySelectMenuAddEntityView().click();
+ setCountry();
+ countries().get(0).click();
+ }
+
+ public void assignedDashboard() {
+ plusBtn().click();
+ assignedField().click();
+ setDashboard();
+ listOfEntity().get(0).click();
+ submitAssignedBtn().click();
+ }
+
+ public boolean customerIsNotPresent(String title) {
+ return elementsIsNotPresent(getEntity(title));
+ }
+
+ public void sortByNameDown() {
+ doubleClick(sortByTitleBtn());
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageElements.java
new file mode 100644
index 0000000000..80bd3780c7
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageElements.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.List;
+
+public class DashboardPageElements extends OtherPageElementsHelper {
+ public DashboardPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String TITLES = "//mat-cell[contains(@class,'cdk-column-title')]/span";
+ private static final String ASSIGNED_BTN = ENTITY + "/../..//mat-icon[contains(text(),' assignment_ind')]/../..";
+ private static final String MANAGE_ASSIGNED_ENTITY_LIST_FIELD = "//input[@formcontrolname='entity']";
+ private static final String MANAGE_ASSIGNED_ENTITY = "//mat-option//span[contains(text(),'%s')]";
+ private static final String MANAGE_ASSIGNED_UPDATE_BTN = "//button[@type='submit']";
+
+ public List entityTitles() {
+ return waitUntilVisibilityOfElementsLocated(TITLES);
+ }
+
+ public WebElement assignedBtn(String title) {
+ return waitUntilElementToBeClickable(String.format(ASSIGNED_BTN, title));
+ }
+
+ public WebElement manageAssignedEntityListField() {
+ return waitUntilElementToBeClickable(MANAGE_ASSIGNED_ENTITY_LIST_FIELD);
+ }
+
+ public WebElement manageAssignedEntity(String title) {
+ return waitUntilElementToBeClickable(String.format(MANAGE_ASSIGNED_ENTITY, title));
+ }
+
+ public WebElement manageAssignedUpdateBtn() {
+ return waitUntilElementToBeClickable(MANAGE_ASSIGNED_UPDATE_BTN);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageHelper.java
new file mode 100644
index 0000000000..1b4ae6dfa9
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DashboardPageHelper.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+
+public class DashboardPageHelper extends DashboardPageElements {
+ public DashboardPageHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ private String dashboardTitle;
+
+ public void setDashboardTitle() {
+ this.dashboardTitle = entityTitles().get(0).getText();
+ }
+
+ public String getDashboardTitle() {
+ return dashboardTitle;
+ }
+
+ public void assignedCustomer(String title) {
+ manageAssignedEntityListField().click();
+ manageAssignedEntity(title).click();
+ manageAssignedUpdateBtn().click();
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageElements.java
new file mode 100644
index 0000000000..b6a2f09513
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageElements.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.thingsboard.server.msa.ui.base.AbstractBasePage;
+
+public class LoginPageElements extends AbstractBasePage {
+ public LoginPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String EMAIL_FIELD = "//input[@id='username-input']";
+ private static final String PASSWORD_FIELD = "//input[@id='password-input']";
+ private static final String SUBMIT_BTN = "//button[@type='submit']";
+
+ public WebElement emailField() {
+ return waitUntilElementToBeClickable(EMAIL_FIELD);
+ }
+
+ public WebElement passwordField() {
+ return waitUntilElementToBeClickable(PASSWORD_FIELD);
+ }
+
+ public WebElement submitBtn() {
+ return waitUntilElementToBeClickable(SUBMIT_BTN);
+ }
+
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java
new file mode 100644
index 0000000000..e59d07e455
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/LoginPageHelper.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.thingsboard.server.msa.ui.utils.Const;
+
+public class LoginPageHelper extends LoginPageElements {
+ public LoginPageHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ public void authorizationTenant() {
+ emailField().sendKeys(Const.TENANT_EMAIL);
+ passwordField().sendKeys(Const.TENANT_PASSWORD);
+ submitBtn().click();
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageElements.java
new file mode 100644
index 0000000000..7fa0bd7468
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageElements.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.thingsboard.server.msa.ui.base.AbstractBasePage;
+
+public class OpenRuleChainPageElements extends AbstractBasePage {
+ public OpenRuleChainPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String DONE_BTN = "//mat-icon[contains(text(),'done')]/../..";
+ private static final String DONE_BTN_DISABLE = "//mat-icon[contains(text(),'done')]/../parent::button[@disabled='true']";
+ private static final String INPUT_NODE = "//div[@class='tb-rule-node tb-input-type']";
+ private static final String HEAD_RULE_CHAIN_NAME = "//div[@class='tb-breadcrumb']/span[2]";
+
+ public WebElement inputNode() {
+ return waitUntilVisibilityOfElementLocated(INPUT_NODE);
+ }
+
+ public WebElement headRuleChainName() {
+ return waitUntilVisibilityOfElementLocated(HEAD_RULE_CHAIN_NAME);
+ }
+
+ public String getDoneBtnDisable() {
+ return DONE_BTN_DISABLE;
+ }
+
+ public WebElement doneBtn() {
+ return waitUntilElementToBeClickable(DONE_BTN);
+ }
+
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageHelper.java
new file mode 100644
index 0000000000..0017396a8a
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OpenRuleChainPageHelper.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+
+public class OpenRuleChainPageHelper extends OpenRuleChainPageElements {
+ public OpenRuleChainPageHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ private String headName;
+
+ public void setHeadName() {
+ this.headName = headRuleChainName().getText().split(" ")[1];
+ }
+
+ public String getHeadName() {
+ return headName;
+ }
+
+ public void waitUntilDoneBtnDisable() {
+ waitUntilVisibilityOfElementLocated(getDoneBtnDisable());
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElements.java
new file mode 100644
index 0000000000..b6ffd2a14e
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElements.java
@@ -0,0 +1,249 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.thingsboard.server.msa.ui.base.AbstractBasePage;
+
+import java.util.List;
+
+public class OtherPageElements extends AbstractBasePage {
+ public OtherPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ protected static final String ENTITY = "//mat-row//span[contains(text(),'%s')]";
+ protected static final String DELETE_BTN = ENTITY + "/../..//mat-icon[contains(text(),' delete')]/../..";
+ protected static final String DETAILS_BTN = ENTITY + "/../..//mat-icon[contains(text(),'edit')]/../..";
+ private static final String ENTITY_COUNT = "//div[@class='mat-paginator-range-label']";
+ private static final String WARNING_DELETE_POPUP_YES = "//tb-confirm-dialog//button[2]";
+ private static final String WARNING_DELETE_POPUP_TITLE = "//tb-confirm-dialog/h2";
+ private static final String REFRESH_BTN = "//mat-icon[contains(text(),'refresh')]/..";
+ private static final String HELP_BTN = "//mat-icon[contains(text(),'help')]/..";
+ private static final String CHECKBOX = "//mat-row//span[contains(text(),'%s')]/../..//mat-checkbox";
+ private static final String CHECKBOXES = "//tbody//mat-checkbox";
+ private static final String DELETE_SELECTED_BTN = "//span[contains(text(),'selected')]//..//mat-icon/../..";
+ private static final String DELETE_BTNS = "//mat-icon[contains(text(),' delete')]/../..";
+ private static final String MARKS_CHECKBOX = "//mat-row[contains (@class,'mat-selected')]//mat-checkbox[contains(@class, 'checked')]";
+ private static final String SELECT_ALL_CHECKBOX = "//thead//mat-checkbox";
+ private static final String ALL_ENTITY = "//mat-row[@class='mat-row cdk-row mat-row-select ng-star-inserted']";
+ private static final String EDIT_PENCIL_BTN = "//mat-icon[contains(text(),'edit')]/ancestor::button";
+ private static final String NAME_FIELD_EDIT_VIEW = "//input[@formcontrolname='name']";
+ private static final String HEADER_NAME_VIEW = "//header//div[@class='tb-details-title']/span";
+ private static final String DONE_BTN_EDIT_VIEW = "//mat-icon[contains(text(),'done')]/ancestor::button";
+ private static final String DESCRIPTION_ENTITY_VIEW = "//textarea";
+ private static final String DESCRIPTION_ADD_ENTITY_VIEW = "//tb-add-entity-dialog//textarea";
+ private static final String DEBUG_CHECKBOX_EDIT = "//mat-checkbox[@formcontrolname='debugMode']";
+ private static final String DEBUG_CHECKBOX_VIEW = "//mat-checkbox[@formcontrolname='debugMode']//input";
+ private static final String CLOSE_ENTITY_VIEW_BTN = "//header//mat-icon[contains(text(),'close')]/../..";
+ private static final String SEARCH_BTN = "//mat-toolbar//mat-icon[contains(text(),'search')]/.." +
+ "/parent::button[@class='mat-focus-indicator mat-tooltip-trigger mat-icon-button mat-button-base ng-star-inserted']";
+ private static final String SORT_BY_NAME_BTN = "//div[contains(text(),'Name')]";
+ private static final String SORT_BY_TITLE_BTN = "//div[contains(text(),'Title')]";
+ private static final String SORT_BY_TIME_BTN = "//div[contains(text(),'Created time')]/..";
+ private static final String CREATED_TIME = "//tbody[@role='rowgroup']//mat-cell[2]/span";
+ private static final String PLUS_BTN = "//mat-icon[contains(text(),'add')]/../parent::button";
+ private static final String CREATE_VIEW_ADD_BTN = "//span[contains(text(),'Add')]/..";
+ private static final String WARNING_MESSAGE = "//tb-snack-bar-component/div/div";
+ private static final String ERROR_MESSAGE = "//mat-error";
+ private static final String ENTITY_VIEW_TITLE = "//div[@class='tb-details-title']//span";
+ private static final String LIST_OF_ENTITY = "//div[@role='listbox']/mat-option";
+ protected static final String ADD_ENTITY_VIEW = "//tb-add-entity-dialog";
+ protected static final String STATE_CONTROLLER = "//tb-entity-state-controller";
+ private static final String SEARCH_FIELD = "//input[contains (@data-placeholder,'Search')]";
+
+ public String getEntity(String entityName) {
+ return String.format(ENTITY, entityName);
+ }
+
+ public String getWarningMessage() {
+ return WARNING_MESSAGE;
+ }
+
+ public String getDeleteBtns() {
+ return DELETE_BTNS;
+ }
+
+ public String getCheckbox(String entityName) {
+ return String.format(CHECKBOX, entityName);
+ }
+
+ public String getCheckboxes() {
+ return String.format(CHECKBOXES);
+ }
+
+ public WebElement warningPopUpYesBtn() {
+ return waitUntilElementToBeClickable(WARNING_DELETE_POPUP_YES);
+ }
+
+ public WebElement warningPopUpTitle() {
+ return waitUntilElementToBeClickable(WARNING_DELETE_POPUP_TITLE);
+ }
+
+ public WebElement entityCount() {
+ return waitUntilVisibilityOfElementLocated(ENTITY_COUNT);
+ }
+
+ public WebElement refreshBtn() {
+ return waitUntilElementToBeClickable(REFRESH_BTN);
+ }
+
+ public WebElement helpBtn() {
+ return waitUntilElementToBeClickable(HELP_BTN);
+ }
+
+ public WebElement checkBox(String entityName) {
+ return waitUntilElementToBeClickable(String.format(CHECKBOX, entityName));
+ }
+
+ public WebElement deleteSelectedBtn() {
+ return waitUntilElementToBeClickable(DELETE_SELECTED_BTN);
+ }
+
+ public WebElement selectAllCheckBox() {
+ return waitUntilElementToBeClickable(SELECT_ALL_CHECKBOX);
+ }
+
+ public WebElement editPencilBtn() {
+ waitUntilVisibilityOfElementsLocated(EDIT_PENCIL_BTN);
+ return waitUntilElementToBeClickable(EDIT_PENCIL_BTN);
+ }
+
+ public WebElement nameFieldEditMenu() {
+ return waitUntilElementToBeClickable(NAME_FIELD_EDIT_VIEW);
+ }
+
+ public WebElement headerNameView() {
+ return waitUntilVisibilityOfElementLocated(HEADER_NAME_VIEW);
+ }
+
+ public WebElement doneBtnEditView() {
+ return waitUntilElementToBeClickable(DONE_BTN_EDIT_VIEW);
+ }
+
+ public WebElement descriptionEntityView() {
+ return waitUntilVisibilityOfElementLocated(DESCRIPTION_ENTITY_VIEW);
+ }
+
+ public WebElement descriptionAddEntityView() {
+ return waitUntilVisibilityOfElementLocated(DESCRIPTION_ADD_ENTITY_VIEW);
+ }
+
+ public WebElement debugCheckboxEdit() {
+ return waitUntilElementToBeClickable(DEBUG_CHECKBOX_EDIT);
+ }
+
+ public WebElement debugCheckboxView() {
+ return waitUntilVisibilityOfElementLocated(DEBUG_CHECKBOX_VIEW);
+ }
+
+ public WebElement closeEntityViewBtn() {
+ return waitUntilElementToBeClickable(CLOSE_ENTITY_VIEW_BTN);
+ }
+
+ public WebElement searchBtn() {
+ return waitUntilElementToBeClickable(SEARCH_BTN);
+ }
+
+ public List deleteBtns() {
+ return waitUntilVisibilityOfElementsLocated(DELETE_BTNS);
+ }
+
+ public List checkBoxes() {
+ return waitUntilElementsToBeClickable(CHECKBOXES);
+ }
+
+ public List markCheckbox() {
+ return waitUntilVisibilityOfElementsLocated(MARKS_CHECKBOX);
+ }
+
+ public List allEntity() {
+ return waitUntilVisibilityOfElementsLocated(ALL_ENTITY);
+ }
+
+ public WebElement doneBtnEditViewVisible() {
+ return waitUntilVisibilityOfElementLocated(DONE_BTN_EDIT_VIEW);
+ }
+
+ public WebElement sortByNameBtn() {
+ return waitUntilElementToBeClickable(SORT_BY_NAME_BTN);
+ }
+
+ public WebElement sortByTitleBtn() {
+ return waitUntilElementToBeClickable(SORT_BY_TITLE_BTN);
+ }
+
+ public WebElement sortByTimeBtn() {
+ return waitUntilElementToBeClickable(SORT_BY_TIME_BTN);
+ }
+
+ public List createdTime() {
+ return waitUntilVisibilityOfElementsLocated(CREATED_TIME);
+ }
+
+ public WebElement plusBtn() {
+ return waitUntilElementToBeClickable(PLUS_BTN);
+ }
+
+ public WebElement addBtnC() {
+ return waitUntilElementToBeClickable(CREATE_VIEW_ADD_BTN);
+ }
+
+ public WebElement addBtnV() {
+ return waitUntilVisibilityOfElementLocated(CREATE_VIEW_ADD_BTN);
+ }
+
+ public WebElement warningMessage() {
+ return waitUntilVisibilityOfElementLocated(WARNING_MESSAGE);
+ }
+
+ public WebElement deleteBtn(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(DELETE_BTN, entityName));
+ }
+
+ public WebElement detailsBtn(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(DETAILS_BTN, entityName));
+ }
+
+ public WebElement entity(String entityName) {
+ return waitUntilElementToBeClickable(String.format(ENTITY, entityName));
+ }
+
+ public WebElement errorMessage() {
+ return waitUntilVisibilityOfElementLocated(ERROR_MESSAGE);
+ }
+
+ public WebElement entityViewTitle() {
+ return waitUntilVisibilityOfElementLocated(ENTITY_VIEW_TITLE);
+ }
+
+ public List listOfEntity() {
+ return waitUntilElementsToBeClickable(LIST_OF_ENTITY);
+ }
+
+ public WebElement addEntityView() {
+ return waitUntilVisibilityOfElementLocated(ADD_ENTITY_VIEW);
+ }
+
+ public WebElement stateController() {
+ return waitUntilVisibilityOfElementLocated(STATE_CONTROLLER);
+ }
+
+ public WebElement searchField() {
+ return waitUntilElementToBeClickable(SEARCH_FIELD);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElementsHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElementsHelper.java
new file mode 100644
index 0000000000..dbef06e7fe
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/OtherPageElementsHelper.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebDriver;
+
+public class OtherPageElementsHelper extends OtherPageElements {
+ public OtherPageElementsHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ private String headerName;
+
+ public void setHeaderName() {
+ this.headerName = headerNameView().getText();
+ }
+
+ public String getHeaderName() {
+ return headerName;
+ }
+
+ public boolean entityIsNotPresent(String entityName) {
+ return elementIsNotPresent(getEntity(entityName));
+ }
+
+ public void goToHelpPage() {
+ helpBtn().click();
+ goToNextTab(2);
+ }
+
+ public void clickOnCheckBoxes(int count) {
+ for (int i = 0; i < count; i++) {
+ checkBoxes().get(i).click();
+ }
+ }
+
+ public void changeNameEditMenu(String newName) {
+ nameFieldEditMenu().sendKeys(Keys.CONTROL + "a" + Keys.BACK_SPACE);
+ nameFieldEditMenu().sendKeys(newName);
+ }
+
+ public void changeDescription(String newDescription) {
+ descriptionEntityView().sendKeys(Keys.CONTROL + "a" + Keys.BACK_SPACE);
+ descriptionEntityView().sendKeys(newDescription);
+ }
+
+ public String deleteRuleChainTrash(String entityName) {
+ String s = "";
+ if (deleteBtn(entityName) != null) {
+ deleteBtn(entityName).click();
+ warningPopUpYesBtn().click();
+ return entityName;
+ } else {
+ for (int i = 0; i < deleteBtns().size(); i++) {
+ if (deleteBtns().get(i).isEnabled()) {
+ deleteBtns().get(i).click();
+ warningPopUpYesBtn().click();
+ if (elementIsNotPresent(getWarningMessage())) {
+ s = driver.findElements(By.xpath(getDeleteBtns()
+ + "/../../../mat-cell/following-sibling::mat-cell/following-sibling::mat-cell[contains(@class,'cdk-column-name')]/span")).get(i).getText();
+ break;
+ }
+ }
+ }
+ return s;
+ }
+ }
+
+ public String deleteSelected(String entityName) {
+ String s = "";
+ if (deleteBtn(entityName) != null) {
+ checkBox(entityName).click();
+ deleteSelectedBtn().click();
+ warningPopUpYesBtn().click();
+ return entityName;
+ } else {
+ for (int i = 0; i < checkBoxes().size(); i++) {
+ if (checkBoxes().get(i).isDisplayed()) {
+ s = driver.findElements(By.xpath(getCheckboxes() + "/../../mat-cell/following-sibling::mat-cell/following-sibling::mat-cell[contains(@class,'cdk-column-name')]/span")).get(i).getText();
+ checkBox(s).click();
+ deleteSelectedBtn().click();
+ warningPopUpYesBtn().click();
+ if (elementIsNotPresent(getWarningMessage())) {
+ break;
+ }
+ }
+ }
+ return s;
+ }
+ }
+
+ public void searchEntity(String namePath) {
+ searchBtn().click();
+ searchField().sendKeys(namePath);
+ sleep(0.5);
+ }
+}
+
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageElements.java
new file mode 100644
index 0000000000..eee95edcae
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageElements.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import java.util.List;
+
+public class RuleChainsPageElements extends OtherPageElementsHelper {
+ public RuleChainsPageElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String MAKE_ROOT_BTN = ENTITY + "/../..//mat-icon[contains(text(),' flag')]/../..";
+ private static final String ROOT = ENTITY + "/../..//mat-icon[text() = 'check_box']";
+ private static final String ROOT_DISABLE = ENTITY + "/../..//mat-icon[text() = 'check_box_outline_blank']";
+ private static final String CREATED_TIME = ENTITY + "/../..//mat-cell/span[contains(text(),'%s')]";
+ private static final String CREATE_RULE_CHAIN_BTN = "//span[contains(text(),'Create new rule chain')]";
+ private static final String CREATE_RULE_CHAIN_NAME_FIELD = "//form[@class='ng-untouched ng-pristine ng-invalid']//input[@formcontrolname='name']";
+ private static final String RULE_CHAINS_NAMES_WITHOUT_ROOT = "//mat-icon[contains(text(),'check_box_outline_blank')]/../../../mat-cell[contains(@class,'name')]/span";
+ private static final String DELETE_RULE_CHAIN_FROM_VIEW_BTN = "//span[contains(text(),' Delete')]";
+ private static final String IMPORT_RULE_CHAIN_BTN = "//span[contains(text(),'Import rule chain')]";
+ private static final String BROWSE_FILE = "//input[@class='file-input']";
+ private static final String IMPORT_BROWSE_FILE = "//mat-dialog-container//span[contains(text(),'Import')]/..";
+ private static final String IMPORTING_FILE = "//div[contains(text(),'%s')]";
+ private static final String CLEAR_IMPORT_FILE_BTN = "//div[@class='tb-file-clear-container']//button";
+ private static final String OPEN_RULE_CHAIN = ENTITY + "/../..//mat-icon[contains(text(),' settings_ethernet')]";
+ private static final String OPEN_RULE_CHAIN_FROM_VIEW = "//span[contains(text(),'Open rule chain')]";
+ private static final String MAKE_ROOT_FROM_VIEW = "(//span[contains(text(),' Make rule chain root ')]/..)[1]";
+ private static final String ROOT_ACTIVE_CHECKBOXES = "//mat-icon[text() = 'check_box']";
+ private static final String ALL_NAMES = "//mat-icon[contains(text(),'check')]/../../../mat-cell[contains(@class,'name')]/span";
+
+ public String getDeleteRuleChainFromViewBtn() {
+ return DELETE_RULE_CHAIN_FROM_VIEW_BTN;
+ }
+
+ public WebElement makeRootBtn(String entityName) {
+ return waitUntilElementToBeClickable(String.format(MAKE_ROOT_BTN, entityName));
+ }
+
+ public List rootCheckBoxesEnable() {
+ return waitUntilVisibilityOfElementsLocated(ROOT_ACTIVE_CHECKBOXES);
+ }
+
+ public WebElement rootCheckBoxEnable(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(ROOT, entityName));
+ }
+
+ public WebElement rootCheckBoxDisable(String entityName) {
+ return waitUntilVisibilityOfElementLocated(String.format(ROOT_DISABLE, entityName));
+ }
+
+ public WebElement createRuleChainBtn() {
+ return waitUntilElementToBeClickable(CREATE_RULE_CHAIN_BTN);
+ }
+
+ public WebElement importRuleChainBtn() {
+ return waitUntilElementToBeClickable(IMPORT_RULE_CHAIN_BTN);
+ }
+
+ public WebElement nameField() {
+ return waitUntilElementToBeClickable(CREATE_RULE_CHAIN_NAME_FIELD);
+ }
+
+ public List notRootRuleChainsNames() {
+ return waitUntilVisibilityOfElementsLocated(RULE_CHAINS_NAMES_WITHOUT_ROOT);
+ }
+
+ public WebElement deleteBtnFromView() {
+ return waitUntilElementToBeClickable(DELETE_RULE_CHAIN_FROM_VIEW_BTN);
+ }
+
+ public WebElement browseFile() {
+ waitUntilElementToBeClickable(BROWSE_FILE + "/preceding-sibling::button");
+ return driver.findElement(By.xpath(BROWSE_FILE));
+ }
+
+ public WebElement importBrowseFileBtn() {
+ return waitUntilElementToBeClickable(IMPORT_BROWSE_FILE);
+ }
+
+ public WebElement importingFile(String fileName) {
+ return waitUntilVisibilityOfElementLocated(String.format(IMPORTING_FILE, fileName));
+ }
+
+ public WebElement clearImportFileBtn() {
+ return waitUntilElementToBeClickable(CLEAR_IMPORT_FILE_BTN);
+ }
+
+ public WebElement openRuleChainFromViewBtn() {
+ return waitUntilElementToBeClickable(OPEN_RULE_CHAIN_FROM_VIEW);
+ }
+
+ public WebElement openRuleChainBtn(String name) {
+ return waitUntilElementToBeClickable(String.format(OPEN_RULE_CHAIN, name));
+ }
+
+ public List entities(String name) {
+ return waitUntilVisibilityOfElementsLocated(String.format(ENTITY, name));
+ }
+
+ public WebElement makeRootFromViewBtn() {
+ return waitUntilElementToBeClickable(MAKE_ROOT_FROM_VIEW);
+ }
+
+ public List allNames() {
+ return waitUntilVisibilityOfElementsLocated(ALL_NAMES);
+ }
+
+ public WebElement createdTimeEntity(String name, String time) {
+ return waitUntilElementToBeClickable(String.format(CREATED_TIME, name, time));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageHelper.java
new file mode 100644
index 0000000000..a6a7148bc4
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/RuleChainsPageHelper.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import lombok.extern.slf4j.Slf4j;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.testng.Assert;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Random;
+
+@Slf4j
+public class RuleChainsPageHelper extends RuleChainsPageElements {
+ public RuleChainsPageHelper(WebDriver driver) {
+ super(driver);
+ }
+
+ public void openCreateRuleChainView() {
+ plusBtn().click();
+ createRuleChainBtn().click();
+ }
+
+ public void openImportRuleChainView() {
+ plusBtn().click();
+ importRuleChainBtn().click();
+ }
+
+ private int getRandomNumberFromRuleChainsCount() {
+ Random random = new Random();
+ return random.nextInt(notRootRuleChainsNames().size());
+ }
+
+ private String ruleChainName;
+
+ public void setRuleChainNameWithoutRoot() {
+ this.ruleChainName = notRootRuleChainsNames().get(getRandomNumberFromRuleChainsCount()).getText();
+ }
+
+ public void setRuleChainNameWithoutRoot(int number) {
+ this.ruleChainName = notRootRuleChainsNames().get(number).getText();
+ }
+
+ public void setRuleChainName(int number) {
+ this.ruleChainName = allNames().get(number).getText();
+ }
+
+ public String getRuleChainName() {
+ return this.ruleChainName;
+ }
+
+ public String deleteRuleChainFromView(String ruleChainName) {
+ String s = "";
+ if (deleteBtnFromView() != null) {
+ deleteBtnFromView().click();
+ warningPopUpYesBtn().click();
+ if (elementIsNotPresent(getWarningMessage())) {
+ return getEntity(ruleChainName);
+ }
+ } else {
+ for (int i = 0; i < notRootRuleChainsNames().size(); i++) {
+ notRootRuleChainsNames().get(i).click();
+ if (deleteBtnFromView() != null) {
+ deleteBtnFromView().click();
+ warningPopUpYesBtn().click();
+ if (elementIsNotPresent(getWarningMessage())) {
+ s = notRootRuleChainsNames().get(i).getText();
+ break;
+ }
+ }
+ }
+ }
+ return s;
+ }
+
+ public void assertCheckBoxIsNotDisplayed(String entityName) {
+ wait.until(ExpectedConditions.elementToBeClickable(By.xpath("(//mat-checkbox)[2]")));
+ Assert.assertFalse(driver.findElement(By.xpath(getCheckbox(entityName))).isDisplayed());
+ }
+
+ public boolean deleteBtnInRootRuleChainIsNotDisplayed() {
+ return wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath(getDeleteRuleChainFromViewBtn())));
+ }
+
+ public boolean ruleChainsIsNotPresent(String ruleChainName) {
+ return elementsIsNotPresent(getEntity(ruleChainName));
+ }
+
+ public void doubleClickOnRuleChain(String ruleChainName) {
+ doubleClick(entity(ruleChainName));
+ }
+
+ public void sortByNameDown() {
+ doubleClick(sortByNameBtn());
+ }
+
+ ArrayList sort;
+
+ public void setSort() {
+ ArrayList createdTime = new ArrayList<>();
+ createdTime().forEach(x -> createdTime.add(x.getText()));
+ Collections.sort(createdTime);
+ sort = createdTime;
+ }
+
+ public ArrayList getSort() {
+ return sort;
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java
new file mode 100644
index 0000000000..902966a1ac
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/SideBarMenuViewElements.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.pages;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.thingsboard.server.msa.ui.base.AbstractBasePage;
+
+public class SideBarMenuViewElements extends AbstractBasePage {
+ public SideBarMenuViewElements(WebDriver driver) {
+ super(driver);
+ }
+
+ private static final String RULE_CHAINS_BTN = "//mat-toolbar//a[@href='/ruleChains']";
+ private static final String CUSTOMER_BTN = "//mat-toolbar//a[@href='/customers']";
+ private static final String DASHBOARD_BTN = "//mat-toolbar//a[@href='/dashboards']";
+
+ public WebElement ruleChainsBtn() {
+ return waitUntilElementToBeClickable(RULE_CHAINS_BTN);
+ }
+
+ public WebElement customerBtn() {
+ return waitUntilElementToBeClickable(CUSTOMER_BTN);
+ }
+
+ public WebElement dashboardBtn() {
+ return waitUntilElementToBeClickable(DASHBOARD_BTN);
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java
new file mode 100644
index 0000000000..50aac8de11
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CreateCustomerTest.java
@@ -0,0 +1,185 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_CUSTOMER_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.SAME_NAME_WARNING_CUSTOMER_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+
+public class CreateCustomerTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private String customerName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (customerName != null) {
+ testRestClient.deleteCustomer(getCustomerByName(customerName).getId());
+ customerName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void createCustomer() {
+ String customerName = ENTITY_NAME;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.plusBtn().click();
+ customerPage.titleFieldAddEntityView().sendKeys(customerName);
+ customerPage.addBtnC().click();
+ this.customerName = customerName;
+ customerPage.refreshBtn().click();
+
+ Assert.assertNotNull(customerPage.customer(customerName));
+ Assert.assertTrue(customerPage.customer(customerName).isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createCustomerWithFullInformation() {
+ String customerName = ENTITY_NAME;
+ String text = "Text";
+ String email = "email@mail.com";
+ String number = "12015550123";
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.plusBtn().click();
+ customerPage.titleFieldAddEntityView().sendKeys(customerName);
+ customerPage.selectCountryAddEntityView();
+ customerPage.descriptionAddEntityView().sendKeys(text);
+ customerPage.cityAddEntityView().sendKeys(text);
+ customerPage.stateAddEntityView().sendKeys(text);
+ customerPage.zipAddEntityView().sendKeys(text);
+ customerPage.addressAddEntityView().sendKeys(text);
+ customerPage.address2AddEntityView().sendKeys(text);
+ customerPage.phoneNumberAddEntityView().sendKeys(number);
+ customerPage.emailAddEntityView().sendKeys(email);
+ customerPage.addBtnC().click();
+ this.customerName = customerName;
+ customerPage.setCustomerEmail(customerName);
+ customerPage.setCustomerCountry(customerName);
+ customerPage.setCustomerCity(customerName);
+ customerPage.entity(customerName).click();
+
+ Assert.assertNotNull(customerPage.customer(customerName));
+ Assert.assertEquals(customerPage.entityViewTitle().getText(), customerName);
+ Assert.assertEquals(customerPage.titleFieldEntityView().getAttribute("value"), customerName);
+ Assert.assertEquals(customerPage.countrySelectMenuEntityView().getText(), customerPage.getCountry());
+ Assert.assertEquals(customerPage.descriptionEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.cityEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.stateEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.zipEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.addressEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.address2EntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.phoneNumberEntityView().getAttribute("value"), "+" + number);
+ Assert.assertEquals(customerPage.emailEntityView().getAttribute("value"), email);
+ Assert.assertEquals(customerPage.getCustomerEmail(), email);
+ Assert.assertEquals(customerPage.getCustomerCountry(), customerPage.getCountry());
+ Assert.assertEquals(customerPage.getCustomerCity(), text);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createCustomerWithoutName() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.plusBtn().click();
+
+ Assert.assertFalse(customerPage.addBtnV().isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createCustomerWithOnlySpace() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.plusBtn().click();
+ customerPage.titleFieldAddEntityView().sendKeys(" ");
+ customerPage.addBtnC().click();
+
+ Assert.assertNotNull(customerPage.warningMessage());
+ Assert.assertTrue(customerPage.warningMessage().isDisplayed());
+ Assert.assertEquals(customerPage.warningMessage().getText(), EMPTY_CUSTOMER_MESSAGE);
+ Assert.assertNotNull(customerPage.addEntityView());
+ Assert.assertTrue(customerPage.addEntityView().isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createCustomerSameName() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ String customerName = customerPage.getCustomerName();
+ customerPage.plusBtn().click();
+ customerPage.titleFieldAddEntityView().sendKeys(customerName);
+ customerPage.addBtnC().click();
+
+ Assert.assertNotNull(customerPage.warningMessage());
+ Assert.assertTrue(customerPage.warningMessage().isDisplayed());
+ Assert.assertEquals(customerPage.warningMessage().getText(), SAME_NAME_WARNING_CUSTOMER_MESSAGE);
+ Assert.assertNotNull(customerPage.addEntityView());
+ Assert.assertTrue(customerPage.addEntityView().isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createCustomerWithoutRefresh() {
+ String customerName = ENTITY_NAME;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.plusBtn().click();
+ customerPage.titleFieldAddEntityView().sendKeys(customerName);
+ customerPage.addBtnC().click();
+ this.customerName = customerName;
+
+ Assert.assertNotNull(customerPage.customer(customerName));
+ Assert.assertTrue(customerPage.customer(customerName).isDisplayed());
+ }
+
+ @Test(priority = 40, groups = "smoke")
+ @Description
+ public void documentation() {
+ String urlPath = "docs/user-guide/ui/customers/";
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.customer(customerPage.getCustomerName()).click();
+ customerPage.goToHelpPage();
+
+ Assert.assertTrue(urlContains(urlPath));
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java
new file mode 100644
index 0000000000..66a5bd5004
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/CustomerEditMenuTest.java
@@ -0,0 +1,293 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.DashboardPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.DataProviderCredential;
+
+import static org.thingsboard.server.msa.ui.base.AbstractBasePage.getRandomNumber;
+import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_CUSTOMER_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.PHONE_NUMBER_ERROR_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultCustomerPrototype;
+
+public class CustomerEditMenuTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private DashboardPageHelper dashboardPage;
+ private String customerName;
+
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ dashboardPage = new DashboardPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (customerName != null) {
+ testRestClient.deleteCustomer(getCustomerByName(customerName).getId());
+ customerName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void changeTitle() {
+ String customerName = "Changed" + getRandomNumber();
+ testRestClient.postCustomer(defaultCustomerPrototype(ENTITY_NAME));
+ this.customerName = customerName;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.setHeaderName();
+ String titleBefore = customerPage.getHeaderName();
+ customerPage.editPencilBtn().click();
+ customerPage.changeTitleEditMenu(customerName);
+ customerPage.doneBtnEditView().click();
+ customerPage.setHeaderName();
+ String titleAfter = customerPage.getHeaderName();
+
+ Assert.assertNotEquals(titleBefore, titleAfter);
+ Assert.assertEquals(titleAfter, customerName);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void deleteTitle() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.titleFieldEntityView().clear();
+
+ Assert.assertFalse(customerPage.doneBtnEditViewVisible().isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void saveOnlyWithSpace() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.changeTitleEditMenu(" ");
+ customerPage.doneBtnEditView().click();
+ customerPage.setHeaderName();
+
+ Assert.assertNotNull(customerPage.warningMessage());
+ Assert.assertTrue(customerPage.warningMessage().isDisplayed());
+ Assert.assertEquals(customerPage.warningMessage().getText(), EMPTY_CUSTOMER_MESSAGE);
+ Assert.assertEquals(customerPage.getCustomerName(), customerPage.getHeaderName());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void editDescription() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+ String description = "Description";
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.descriptionEntityView().sendKeys(description);
+ customerPage.doneBtnEditView().click();
+ String description1 = customerPage.descriptionEntityView().getAttribute("value");
+ customerPage.editPencilBtn().click();
+ customerPage.descriptionEntityView().sendKeys(description);
+ customerPage.doneBtnEditView().click();
+ String description2 = customerPage.descriptionEntityView().getAttribute("value");
+ customerPage.editPencilBtn().click();
+ customerPage.changeDescription("");
+ customerPage.doneBtnEditView().click();
+
+ Assert.assertEquals(description, description1);
+ Assert.assertEquals(description + description, description2);
+ Assert.assertTrue(customerPage.descriptionEntityView().getAttribute("value").isEmpty());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void assignedDashboardFromDashboard() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+
+ sideBarMenuView.dashboardBtn().click();
+ dashboardPage.setDashboardTitle();
+ dashboardPage.assignedBtn(dashboardPage.getDashboardTitle()).click();
+ dashboardPage.assignedCustomer(customerName);
+ sideBarMenuView.customerBtn().click();
+ customerPage.entity(customerName).click();
+ customerPage.editPencilBtn().click();
+ customerPage.chooseDashboard();
+ customerPage.doneBtnEditView().click();
+ customerPage.setDashboardFromView();
+ customerPage.closeEntityViewBtn().click();
+ customerPage.manageCustomersUserBtn(customerName).click();
+ customerPage.createCustomersUser();
+ customerPage.userLoginBtn().click();
+
+ Assert.assertNotNull(customerPage.usersWidget());
+ Assert.assertTrue(customerPage.usersWidget().isDisplayed());
+ Assert.assertEquals(customerPage.getDashboardFromView(), dashboardPage.getDashboardTitle());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void assignedDashboard() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.manageCustomersDashboardsBtn(customerName).click();
+ customerPage.assignedDashboard();
+ sideBarMenuView.customerBtn().click();
+ customerPage.entity(customerName).click();
+ customerPage.editPencilBtn().click();
+ customerPage.chooseDashboard();
+ customerPage.doneBtnEditView().click();
+ customerPage.setDashboardFromView();
+ customerPage.closeEntityViewBtn().click();
+ customerPage.manageCustomersUserBtn(customerName).click();
+ customerPage.createCustomersUser();
+ customerPage.userLoginBtn().click();
+
+ Assert.assertNotNull(customerPage.usersWidget());
+ Assert.assertTrue(customerPage.usersWidget().isDisplayed());
+ Assert.assertEquals(customerPage.getDashboard(), customerPage.getDashboardFromView());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void assignedDashboardWithoutHide() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.manageCustomersDashboardsBtn(customerName).click();
+ customerPage.assignedDashboard();
+ sideBarMenuView.customerBtn().click();
+ customerPage.entity(customerName).click();
+ customerPage.editPencilBtn().click();
+ customerPage.chooseDashboard();
+ customerPage.hideHomeDashboardToolbarCheckbox().click();
+ customerPage.doneBtnEditView().click();
+ customerPage.setDashboardFromView();
+ customerPage.closeEntityViewBtn().click();
+ customerPage.manageCustomersUserBtn(customerName).click();
+ customerPage.createCustomersUser();
+ customerPage.userLoginBtn().click();
+
+ Assert.assertNotNull(customerPage.usersWidget());
+ Assert.assertTrue(customerPage.usersWidget().isDisplayed());
+ Assert.assertEquals(customerPage.getDashboard(), customerPage.getDashboardFromView());
+ Assert.assertNotNull(customerPage.stateController());
+ Assert.assertNotNull(customerPage.filterBtn());
+ Assert.assertNotNull(customerPage.timeBtn());
+ Assert.assertTrue(customerPage.stateController().isDisplayed());
+ Assert.assertTrue(customerPage.filterBtn().isDisplayed());
+ Assert.assertTrue(customerPage.timeBtn().isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void addPhoneNumber() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+ String number = "2015550123";
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.phoneNumberEntityView().sendKeys(number);
+ customerPage.doneBtnEditView().click();
+
+ Assert.assertTrue(customerPage.phoneNumberEntityView().getAttribute("value").contains(number));
+ }
+
+ @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "incorrectPhoneNumber")
+ @Description
+ public void addIncorrectPhoneNumber(String number) {
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.phoneNumberEntityView().sendKeys(number);
+ boolean doneBtnIsEnable = customerPage.doneBtnEditViewVisible().isEnabled();
+ customerPage.doneBtnEditViewVisible().click();
+
+ Assert.assertFalse(doneBtnIsEnable);
+ Assert.assertNotNull(customerPage.errorMessage());
+ Assert.assertTrue(customerPage.errorMessage().isDisplayed());
+ Assert.assertEquals(customerPage.errorMessage().getText(), PHONE_NUMBER_ERROR_MESSAGE);
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void addAllInformation() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+ this.customerName = customerName;
+ String text = "Text";
+ String email = "email@mail.com";
+ String number = "2015550123";
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.entityTitles().get(0).click();
+ customerPage.editPencilBtn().click();
+ customerPage.selectCountryEntityView();
+ customerPage.descriptionEntityView().sendKeys(text);
+ customerPage.cityEntityView().sendKeys(text);
+ customerPage.stateEntityView().sendKeys(text);
+ customerPage.zipEntityView().sendKeys(text);
+ customerPage.addressEntityView().sendKeys(text);
+ customerPage.address2EntityView().sendKeys(text);
+ customerPage.phoneNumberEntityView().sendKeys(number);
+ customerPage.emailEntityView().sendKeys(email);
+ customerPage.doneBtnEditView().click();
+
+ Assert.assertEquals(customerPage.countrySelectMenuEntityView().getText(), customerPage.getCountry());
+ Assert.assertEquals(customerPage.descriptionEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.cityEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.stateEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.zipEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.addressEntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.address2EntityView().getAttribute("value"), text);
+ Assert.assertEquals(customerPage.phoneNumberEntityView().getAttribute("value"), "+1" + number);
+ Assert.assertEquals(customerPage.emailEntityView().getAttribute("value"), email);
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java
new file mode 100644
index 0000000000..43c6fc8c27
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteCustomerTest.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultCustomerPrototype;
+
+public class DeleteCustomerTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private RuleChainsPageHelper ruleChainsPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void removeCustomerByRightSideBtn() {
+ String customer = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customer));
+
+ sideBarMenuView.customerBtn().click();
+ String deletedCustomer = customerPage.deleteRuleChainTrash(customer);
+ customerPage.refreshBtn().click();
+
+ Assert.assertTrue(customerPage.entityIsNotPresent(deletedCustomer));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeSelectedCustomer() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+
+ sideBarMenuView.customerBtn().click();
+ String deletedCustomer = customerPage.deleteSelected(customerName);
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedCustomer));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeFromCustomerView() {
+ String customerName = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customerName));
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.entity(customerName).click();
+ customerPage.customerViewDeleteBtn().click();
+ customerPage.warningPopUpYesBtn().click();
+ customerPage.refreshBtn().click();
+
+ Assert.assertTrue(customerPage.entityIsNotPresent(customerName));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeCustomerByRightSideBtnWithoutRefresh() {
+ String customer = ENTITY_NAME;
+ testRestClient.postCustomer(defaultCustomerPrototype(customer));
+
+ sideBarMenuView.customerBtn().click();
+ String deletedCustomer = customerPage.deleteRuleChainTrash(customer);
+ customerPage.refreshBtn().click();
+
+ Assert.assertTrue(customerPage.entityIsNotPresent(deletedCustomer));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java
new file mode 100644
index 0000000000..06005075c3
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/DeleteSeveralCustomerTest.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultCustomerPrototype;
+
+public class DeleteSeveralCustomerTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void canDeleteSeveralCustomersByTopBtn() {
+ String title1 = ENTITY_NAME + "1";
+ String title2 = ENTITY_NAME + "2";
+ testRestClient.postCustomer(defaultCustomerPrototype(title1));
+ testRestClient.postCustomer(defaultCustomerPrototype(title2));
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.clickOnCheckBoxes(2);
+ customerPage.deleteSelectedBtn().click();
+ customerPage.warningPopUpYesBtn().click();
+ customerPage.refreshBtn().click();
+
+ Assert.assertTrue(customerPage.customerIsNotPresent(title1));
+ Assert.assertTrue(customerPage.customerIsNotPresent(title2));
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void selectAllCustomers() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.selectAllCheckBox().click();
+ customerPage.deleteSelectedBtn().click();
+
+ Assert.assertNotNull(customerPage.warningPopUpTitle());
+ Assert.assertTrue(customerPage.warningPopUpTitle().isDisplayed());
+ Assert.assertTrue(customerPage.warningPopUpTitle().getText().contains(String.valueOf(customerPage.markCheckbox().size())));
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void deleteSeveralCustomersByTopBtnWithoutRefresh() {
+ String title1 = ENTITY_NAME + "1";
+ String title2 = ENTITY_NAME + "2";
+ testRestClient.postCustomer(defaultCustomerPrototype(title1));
+ testRestClient.postCustomer(defaultCustomerPrototype(title2));
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.clickOnCheckBoxes(2);
+ customerPage.deleteSelectedBtn().click();
+ customerPage.warningPopUpYesBtn().click();
+
+ Assert.assertTrue(customerPage.customerIsNotPresent(title1));
+ Assert.assertTrue(customerPage.customerIsNotPresent(title2));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java
new file mode 100644
index 0000000000..cfcf80c664
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersAssetsTest.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+public class ManageCustomersAssetsTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private final String manage = "Assets";
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByRightCornerBtn() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.manageCustomersAssetsBtn(customerPage.getCustomerName()).click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerAssetsIconHeader());
+ Assert.assertTrue(customerPage.customerAssetsIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByView() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entity(customerPage.getCustomerName()).click();
+ customerPage.manageCustomersAssetsBtnView().click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerAssetsIconHeader());
+ Assert.assertTrue(customerPage.customerAssetsIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java
new file mode 100644
index 0000000000..50b6e895c8
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDashboardsTest.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+public class ManageCustomersDashboardsTest extends AbstractDriverBaseTest {
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private final String manage = "Dashboards";
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByRightCornerBtn() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.manageCustomersDashboardsBtn(customerPage.getCustomerName()).click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerDashboardIconHeader());
+ Assert.assertTrue(customerPage.customerDashboardIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByView() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entity(customerPage.getCustomerName()).click();
+ customerPage.manageCustomersDashboardsBtnView().click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerDashboardIconHeader());
+ Assert.assertTrue(customerPage.customerDashboardIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java
new file mode 100644
index 0000000000..6b5d3b80fd
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersDevicesTest.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+public class ManageCustomersDevicesTest extends AbstractDriverBaseTest {
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private final String manage = "Devices";
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByRightCornerBtn() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.manageCustomersDevicesBtn(customerPage.getCustomerName()).click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerDevicesIconHeader());
+ Assert.assertTrue(customerPage.customerDevicesIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByView() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entity(customerPage.getCustomerName()).click();
+ customerPage.manageCustomersDeviceBtnView().click();
+
+ Assert.assertTrue(urlContains(manage.toLowerCase()));
+ Assert.assertNotNull(customerPage.customerDevicesIconHeader());
+ Assert.assertTrue(customerPage.customerDevicesIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(manage));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java
new file mode 100644
index 0000000000..051658eb00
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersEdgesTest.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+public class ManageCustomersEdgesTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private final String iconText = "Edge instances";
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByRightCornerBtn() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.manageCustomersEdgeBtn(customerPage.getCustomerName()).click();
+
+ Assert.assertTrue(urlContains("edgeInstances"));
+ Assert.assertNotNull(customerPage.customerEdgeIconHeader());
+ Assert.assertTrue(customerPage.customerEdgeIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByView() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entity(customerPage.getCustomerName()).click();
+ customerPage.manageCustomersEdgeBtnView().click();
+
+ Assert.assertTrue(urlContains("edgeInstances"));
+ Assert.assertNotNull(customerPage.customerEdgeIconHeader());
+ Assert.assertTrue(customerPage.customerEdgeIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java
new file mode 100644
index 0000000000..509bacefc0
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/ManageCustomersUsersTest.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+public class ManageCustomersUsersTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private final String iconText = "Customer Users";
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByRightCornerBtn() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.manageCustomersUserBtn(customerPage.getCustomerName()).click();
+
+ Assert.assertTrue(urlContains("user"));
+ Assert.assertNotNull(customerPage.customerUserIconHeader());
+ Assert.assertTrue(customerPage.customerUserIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
+ }
+
+ @Test(groups = "smoke")
+ @Description
+ public void openWindowByView() {
+ sideBarMenuView.customerBtn().click();
+ customerPage.setCustomerName();
+ customerPage.entity(customerPage.getCustomerName()).click();
+ customerPage.manageCustomersUserBtnView().click();
+
+ Assert.assertTrue(urlContains("user"));
+ Assert.assertNotNull(customerPage.customerUserIconHeader());
+ Assert.assertTrue(customerPage.customerUserIconHeader().isDisplayed());
+ Assert.assertTrue(customerPage.customerManageWindowIconHead().getText().contains(iconText));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java
new file mode 100644
index 0000000000..7a1a4694bc
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SearchCustomerTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.DataProviderCredential;
+
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultCustomerPrototype;
+
+public class SearchCustomerTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "customerNameForSearchByFirstAndSecondWord")
+ @Description
+ public void searchFirstWord(String namePath) {
+ sideBarMenuView.customerBtn().click();
+ customerPage.searchEntity(namePath);
+
+ customerPage.allEntity().forEach(x -> Assert.assertTrue(x.getText().contains(namePath)));
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSearchBySymbolAndNumber")
+ @Description
+ public void searchNumber(String name, String namePath) {
+ testRestClient.postCustomer(defaultCustomerPrototype(name));
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.searchEntity(namePath);
+ customerPage.setCustomerName();
+ boolean customerNameContainsPath = customerPage.getCustomerName().contains(namePath);
+
+ testRestClient.deleteCustomer(getCustomerByName(name).getId());
+
+ Assert.assertTrue(customerNameContainsPath);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java
new file mode 100644
index 0000000000..728696ac6f
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/customerSmoke/SortByNameTest.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.customerSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.CustomerPageHelper;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.DataProviderCredential;
+
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultCustomerPrototype;
+
+public class SortByNameTest extends AbstractDriverBaseTest {
+ private SideBarMenuViewElements sideBarMenuView;
+ private CustomerPageHelper customerPage;
+ private String customerName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ customerPage = new CustomerPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (customerName != null) {
+ testRestClient.deleteCustomer(getCustomerByName(customerName).getId());
+ customerName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
+ @Description
+ public void specialCharacterUp(String title) {
+ testRestClient.postCustomer(defaultCustomerPrototype(title));
+ this.customerName = title;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.sortByTitleBtn().click();
+ customerPage.setCustomerName();
+
+ Assert.assertEquals(customerPage.getCustomerName(), title);
+ }
+
+ @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
+ @Description
+ public void allSortUp(String customer, String customerSymbol, String customerNumber) {
+ testRestClient.postCustomer(defaultCustomerPrototype(customerSymbol));
+ testRestClient.postCustomer(defaultCustomerPrototype(customer));
+ testRestClient.postCustomer(defaultCustomerPrototype(customerNumber));
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.sortByTitleBtn().click();
+ customerPage.setCustomerName(0);
+ String firstCustomer = customerPage.getCustomerName();
+ customerPage.setCustomerName(1);
+ String secondCustomer = customerPage.getCustomerName();
+ customerPage.setCustomerName(2);
+ String thirdCustomer = customerPage.getCustomerName();
+
+ testRestClient.deleteCustomer(getCustomerByName(customer).getId());
+ testRestClient.deleteCustomer(getCustomerByName(customerNumber).getId());
+ testRestClient.deleteCustomer(getCustomerByName(customerSymbol).getId());
+
+ Assert.assertEquals(firstCustomer, customerSymbol);
+ Assert.assertEquals(secondCustomer, customerNumber);
+ Assert.assertEquals(thirdCustomer, customer);
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
+ @Description
+ public void specialCharacterDown(String title) {
+ testRestClient.postCustomer(defaultCustomerPrototype(title));
+ customerName = title;
+
+ sideBarMenuView.customerBtn().click();
+ customerPage.sortByNameDown();
+ customerPage.setCustomerName(customerPage.allEntity().size() - 1);
+
+ Assert.assertEquals(customerPage.getCustomerName(), title);
+ }
+
+ @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
+ @Description
+ public void allSortDown(String customer, String customerSymbol, String customerNumber) {
+ testRestClient.postCustomer(defaultCustomerPrototype(customerSymbol));
+ testRestClient.postCustomer(defaultCustomerPrototype(customer));
+ testRestClient.postCustomer(defaultCustomerPrototype(customerNumber));
+
+ sideBarMenuView.customerBtn().click();
+ int lastIndex = customerPage.allEntity().size() - 1;
+ customerPage.sortByNameDown();
+ customerPage.setCustomerName(lastIndex);
+ String firstCustomer = customerPage.getCustomerName();
+ customerPage.setCustomerName(lastIndex - 1);
+ String secondCustomer = customerPage.getCustomerName();
+ customerPage.setCustomerName(lastIndex - 2);
+ String thirdCustomer = customerPage.getCustomerName();
+
+ testRestClient.deleteCustomer(getCustomerByName(customer).getId());
+ testRestClient.deleteCustomer(getCustomerByName(customerNumber).getId());
+ testRestClient.deleteCustomer(getCustomerByName(customerSymbol).getId());
+
+ Assert.assertEquals(firstCustomer, customerSymbol);
+ Assert.assertEquals(secondCustomer, customerNumber);
+ Assert.assertEquals(thirdCustomer, customer);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java
new file mode 100644
index 0000000000..27a4ea2ecb
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainImportTest.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.OpenRuleChainPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import java.util.ArrayList;
+
+import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_IMPORT_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.IMPORT_RULE_CHAIN_FILE_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.IMPORT_RULE_CHAIN_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.IMPORT_TXT_FILE_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class CreateRuleChainImportTest extends AbstractDriverBaseTest {
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private OpenRuleChainPageHelper openRuleChainPage;
+ private final String absolutePathToFileImportRuleChain = getClass().getClassLoader().getResource(IMPORT_RULE_CHAIN_FILE_NAME).getPath();
+ private final String absolutePathToFileImportTxt = getClass().getClassLoader().getResource(IMPORT_TXT_FILE_NAME).getPath();
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ openRuleChainPage = new OpenRuleChainPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void importRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openImportRuleChainView();
+ ruleChainsPage.browseFile().sendKeys(absolutePathToFileImportRuleChain);
+
+ Assert.assertNotNull(ruleChainsPage.importingFile(IMPORT_RULE_CHAIN_FILE_NAME));
+ Assert.assertTrue(ruleChainsPage.importingFile(IMPORT_RULE_CHAIN_FILE_NAME).isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void importRuleChainAndDeleteFile() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openImportRuleChainView();
+ ruleChainsPage.browseFile().sendKeys(absolutePathToFileImportRuleChain);
+ ruleChainsPage.clearImportFileBtn().click();
+
+ Assert.assertNotNull(ruleChainsPage.importingFile(EMPTY_IMPORT_MESSAGE));
+ Assert.assertTrue(ruleChainsPage.importingFile(EMPTY_IMPORT_MESSAGE).isDisplayed());
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(IMPORT_TXT_FILE_NAME));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void importTxtFile() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openImportRuleChainView();
+ ruleChainsPage.browseFile().sendKeys(absolutePathToFileImportTxt);
+
+ Assert.assertNotNull(ruleChainsPage.importingFile(EMPTY_IMPORT_MESSAGE));
+ Assert.assertTrue(ruleChainsPage.importingFile(EMPTY_IMPORT_MESSAGE).isDisplayed());
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void importRuleChainAndSave() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openImportRuleChainView();
+ ruleChainsPage.browseFile().sendKeys(absolutePathToFileImportRuleChain);
+ ruleChainsPage.importBrowseFileBtn().click();
+ openRuleChainPage.doneBtn().click();
+ openRuleChainPage.waitUntilDoneBtnDisable();
+ ruleChainName = IMPORT_RULE_CHAIN_NAME;
+ sideBarMenuView.ruleChainsBtn().click();
+
+ Assert.assertNotNull(ruleChainsPage.entity(IMPORT_RULE_CHAIN_NAME));
+ Assert.assertTrue(ruleChainsPage.entity(IMPORT_RULE_CHAIN_NAME).isDisplayed());
+ }
+
+ @Test(priority = 40, groups = "smoke")
+ @Description
+ public void importRuleChainAndSaveWithSameName() {
+ String ruleChainName = IMPORT_RULE_CHAIN_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openImportRuleChainView();
+ ruleChainsPage.browseFile().sendKeys(absolutePathToFileImportRuleChain);
+ ruleChainsPage.importBrowseFileBtn().click();
+ openRuleChainPage.doneBtn().click();
+ openRuleChainPage.waitUntilDoneBtnDisable();
+ sideBarMenuView.ruleChainsBtn().click();
+
+ boolean entityNotNull = ruleChainsPage.entity(ruleChainName) != null;
+ boolean entitiesSizeMoreOne = ruleChainsPage.entities(ruleChainName).size() > 1;
+ ArrayList entityIsDisplayed = new ArrayList<>();
+ ruleChainsPage.entities(ruleChainName).forEach(x -> entityIsDisplayed.add(x.isDisplayed()));
+
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+
+ Assert.assertTrue(entityNotNull);
+ Assert.assertTrue(entitiesSizeMoreOne);
+ entityIsDisplayed.forEach(Assert::assertTrue);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java
new file mode 100644
index 0000000000..b60b4a4a52
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/CreateRuleChainTest.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.EntityPrototypes;
+
+import java.util.ArrayList;
+
+import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_RULE_CHAIN_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+
+public class CreateRuleChainTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void createRuleChain() {
+ String ruleChainName = ENTITY_NAME;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+ ruleChainsPage.nameField().click();
+ ruleChainsPage.nameField().sendKeys(ruleChainName);
+ ruleChainsPage.addBtnC().click();
+ ruleChainsPage.refreshBtn().click();
+ this.ruleChainName = ruleChainName;
+
+ Assert.assertNotNull(ruleChainsPage.entity(ruleChainName));
+ Assert.assertTrue(ruleChainsPage.entity(ruleChainName).isDisplayed());
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void createRuleChainWithDescription() {
+ String ruleChainName = ENTITY_NAME;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+ ruleChainsPage.nameField().sendKeys(ruleChainName);
+ ruleChainsPage.descriptionAddEntityView().sendKeys(ENTITY_NAME);
+ ruleChainsPage.addBtnC().click();
+ ruleChainsPage.refreshBtn().click();
+ this.ruleChainName = ruleChainName;
+ ruleChainsPage.detailsBtn(ENTITY_NAME).click();
+ ruleChainsPage.setHeaderName();
+
+ Assert.assertEquals(ruleChainsPage.getHeaderName(), ruleChainName);
+ Assert.assertEquals(ruleChainsPage.descriptionEntityView().getAttribute("value"), ruleChainName);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createRuleChainWithoutName() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+
+ Assert.assertFalse(ruleChainsPage.addBtnV().isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createRuleChainWithOnlySpace() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+ ruleChainsPage.nameField().sendKeys(" ");
+ ruleChainsPage.addBtnC().click();
+
+ Assert.assertNotNull(ruleChainsPage.warningMessage());
+ Assert.assertTrue(ruleChainsPage.warningMessage().isDisplayed());
+ Assert.assertEquals(ruleChainsPage.warningMessage().getText(), EMPTY_RULE_CHAIN_MESSAGE);
+ Assert.assertNotNull(ruleChainsPage.addEntityView());
+ Assert.assertTrue(ruleChainsPage.addEntityView().isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void createRuleChainWithSameName() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+ ruleChainsPage.nameField().sendKeys(ruleChainName);
+ ruleChainsPage.addBtnC().click();
+ ruleChainsPage.refreshBtn().click();
+
+ boolean entityNotNull = ruleChainsPage.entity(ruleChainName) != null;
+ boolean entitiesSizeMoreOne = ruleChainsPage.entities(ruleChainName).size() > 1;
+ ArrayList entityIsDisplayed = new ArrayList<>();
+ ruleChainsPage.entities(ruleChainName).forEach(x -> entityIsDisplayed.add(x.isDisplayed()));
+
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+
+ Assert.assertTrue(entityNotNull);
+ Assert.assertTrue(entitiesSizeMoreOne);
+ entityIsDisplayed.forEach(Assert::assertTrue);
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void createRuleChainWithoutRefresh() {
+ String ruleChainName = ENTITY_NAME;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.openCreateRuleChainView();
+ ruleChainsPage.nameField().sendKeys(ruleChainName);
+ ruleChainsPage.addBtnC().click();
+ this.ruleChainName = ruleChainName;
+
+ Assert.assertNotNull(ruleChainsPage.entity(ruleChainName));
+ Assert.assertTrue(ruleChainsPage.entity(ruleChainName).isDisplayed());
+ }
+
+ @Test(priority = 40, groups = "smoke")
+ @Description
+ public void documentation() {
+ String urlPath = "docs/user-guide/ui/rule-chains/";
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.setRuleChainNameWithoutRoot();
+ ruleChainsPage.detailsBtn(ruleChainsPage.getRuleChainName()).click();
+ ruleChainsPage.goToHelpPage();
+
+ Assert.assertTrue(urlContains(urlPath));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java
new file mode 100644
index 0000000000..d605b6c0e9
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteRuleChainTest.java
@@ -0,0 +1,171 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.ROOT_RULE_CHAIN_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class DeleteRuleChainTest extends AbstractDriverBaseTest {
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void removeRuleChainByRightSideBtn() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ String deletedRuleChain = ruleChainsPage.deleteRuleChainTrash(ruleChainName);
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeSelectedRuleChain() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ String deletedRuleChain = ruleChainsPage.deleteSelected(ruleChainName);
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeFromRuleChainView() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ENTITY_NAME).click();
+ String deletedRuleChain = ruleChainsPage.deleteRuleChainFromView(ruleChainName);
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeRootRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+
+ Assert.assertFalse(ruleChainsPage.deleteBtn(ROOT_RULE_CHAIN_NAME).isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeSelectedRootRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+
+ ruleChainsPage.assertCheckBoxIsNotDisplayed(ROOT_RULE_CHAIN_NAME);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeFromRootRuleChainView() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ROOT_RULE_CHAIN_NAME).click();
+ ruleChainsPage.deleteBtnFromView();
+
+ Assert.assertTrue(ruleChainsPage.deleteBtnInRootRuleChainIsNotDisplayed());
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void removeProfileRuleChainByRightSideBtn() {
+ String deletedRuleChain = "Thermostat";
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.deleteBtn(deletedRuleChain).click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertNotNull(ruleChainsPage.entity(deletedRuleChain));
+ Assert.assertTrue(ruleChainsPage.entity(deletedRuleChain).isDisplayed());
+ Assert.assertNotNull(ruleChainsPage.warningMessage());
+ Assert.assertTrue(ruleChainsPage.warningMessage().isDisplayed());
+ Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeSelectedProfileRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+ String deletedRuleChain = ruleChainsPage.deleteSelected("Thermostat");
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertNotNull(ruleChainsPage.entity(deletedRuleChain));
+ Assert.assertTrue(ruleChainsPage.entity(deletedRuleChain).isDisplayed());
+ Assert.assertNotNull(ruleChainsPage.warningMessage());
+ Assert.assertTrue(ruleChainsPage.warningMessage().isDisplayed());
+ Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeFromProfileRuleChainView() {
+ String deletedRuleChain = "Thermostat";
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(deletedRuleChain).click();
+ ruleChainsPage.deleteBtnFromView().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+
+ Assert.assertNotNull(ruleChainsPage.entity(deletedRuleChain));
+ Assert.assertNotNull(ruleChainsPage.warningMessage());
+ Assert.assertTrue(ruleChainsPage.warningMessage().isDisplayed());
+ Assert.assertEquals(ruleChainsPage.warningMessage().getText(), DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE);
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void removeRuleChainByRightSideBtnWithoutRefresh() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ String deletedRuleChain = ruleChainsPage.deleteRuleChainTrash(ruleChainName);
+
+ Assert.assertTrue(ruleChainsPage.entityIsNotPresent(deletedRuleChain));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java
new file mode 100644
index 0000000000..bd6b8f73da
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/DeleteSeveralRuleChainsTest.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.ROOT_RULE_CHAIN_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class DeleteSeveralRuleChainsTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void canDeleteSeveralRuleChainsByTopBtn() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.clickOnCheckBoxes(2);
+ ruleChainsPage.deleteSelectedBtn().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.ruleChainsIsNotPresent(ruleChainName));
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void selectAllRuleChain() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.selectAllCheckBox().click();
+ ruleChainsPage.deleteSelectedBtn().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+ ruleChainsPage.refreshBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.ruleChainsIsNotPresent(ruleChainName));
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeRootRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.selectAllCheckBox().click();
+
+ Assert.assertFalse(ruleChainsPage.deleteBtn(ROOT_RULE_CHAIN_NAME).isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void removeSelectedRootRuleChain() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.selectAllCheckBox().click();
+
+ ruleChainsPage.assertCheckBoxIsNotDisplayed(ROOT_RULE_CHAIN_NAME);
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void deleteSeveralRuleChainsByTopBtnWithoutRefresh() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName + 1));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.clickOnCheckBoxes(2);
+ ruleChainsPage.deleteSelectedBtn().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.ruleChainsIsNotPresent(ruleChainName));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java
new file mode 100644
index 0000000000..f63e65cc25
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/MakeRuleChainRootTest.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+
+public class MakeRuleChainRootTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void makeRoot() {
+ testRestClient.setRootRuleChain(getRuleChainByName("Root Rule Chain").getId());
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void makeRuleChainRootByRightCornerBtn() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.setRuleChainNameWithoutRoot(0);
+ String ruleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.makeRootBtn(ruleChain).click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.rootCheckBoxEnable(ruleChain).isDisplayed());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void makeRuleChainRootFromView() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.setRuleChainNameWithoutRoot(0);
+ String ruleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.detailsBtn(ruleChain).click();
+ ruleChainsPage.makeRootFromViewBtn().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+ ruleChainsPage.closeEntityViewBtn().click();
+
+ Assert.assertTrue(ruleChainsPage.rootCheckBoxEnable(ruleChain).isDisplayed());
+ }
+
+ @Test(priority = 30, groups = "smoke")
+ @Description
+ public void multiplyRoot() {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.setRuleChainNameWithoutRoot(0);
+ String ruleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.detailsBtn(ruleChain).click();
+ ruleChainsPage.makeRootFromViewBtn().click();
+ ruleChainsPage.warningPopUpYesBtn().click();
+ ruleChainsPage.closeEntityViewBtn().click();
+
+ Assert.assertEquals(ruleChainsPage.rootCheckBoxesEnable().size(), 1);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java
new file mode 100644
index 0000000000..2f23fd8b74
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/OpenRuleChainTest.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.OpenRuleChainPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.EntityPrototypes;
+
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+
+public class OpenRuleChainTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private OpenRuleChainPageHelper openRuleChainPage;
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ openRuleChainPage = new OpenRuleChainPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete(){
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void openRuleChainByRightCornerBtn() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ENTITY_NAME));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.entity(ruleChainName).click();
+ openRuleChainPage.setHeadName();
+
+ Assert.assertTrue(urlContains(String.valueOf(getRuleChainByName(ruleChainName).getId())));
+ Assert.assertTrue(openRuleChainPage.headRuleChainName().isDisplayed());
+ Assert.assertTrue(openRuleChainPage.inputNode().isDisplayed());
+ Assert.assertEquals(ruleChainName, openRuleChainPage.getHeadName());
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void openRuleChainByViewBtn() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(EntityPrototypes.defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.openRuleChainFromViewBtn().click();
+ openRuleChainPage.setHeadName();
+
+ Assert.assertTrue(urlContains(String.valueOf(getRuleChainByName(ruleChainName).getId())));
+ Assert.assertTrue(openRuleChainPage.headRuleChainName().isDisplayed());
+ Assert.assertTrue(openRuleChainPage.inputNode().isDisplayed());
+ Assert.assertEquals(ruleChainName, openRuleChainPage.getHeadName());
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java
new file mode 100644
index 0000000000..3a7ea071bb
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/RuleChainEditMenuTest.java
@@ -0,0 +1,160 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_RULE_CHAIN_MESSAGE;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class RuleChainEditMenuTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void changeName() {
+ String newRuleChainName = "Changed";
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.setHeaderName();
+ String nameBefore = ruleChainsPage.getHeaderName();
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.changeNameEditMenu(newRuleChainName);
+ ruleChainsPage.doneBtnEditView().click();
+ this.ruleChainName = newRuleChainName;
+ ruleChainsPage.setHeaderName();
+ String nameAfter = ruleChainsPage.getHeaderName();
+
+ Assert.assertNotEquals(nameBefore, nameAfter);
+ Assert.assertEquals(newRuleChainName, nameAfter);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void deleteName() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.changeNameEditMenu("");
+
+ Assert.assertFalse(ruleChainsPage.doneBtnEditViewVisible().isEnabled());
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void saveOnlyWithSpace() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.changeNameEditMenu(" ");
+ ruleChainsPage.doneBtnEditView().click();
+
+ Assert.assertNotNull(ruleChainsPage.warningMessage());
+ Assert.assertTrue(ruleChainsPage.warningMessage().isDisplayed());
+ Assert.assertEquals(ruleChainsPage.warningMessage().getText(), EMPTY_RULE_CHAIN_MESSAGE);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void editDescription() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+ String description = "Description";
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.descriptionEntityView().sendKeys(description);
+ ruleChainsPage.doneBtnEditView().click();
+ String description1 = ruleChainsPage.descriptionEntityView().getAttribute("value");
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.descriptionEntityView().sendKeys(description);
+ ruleChainsPage.doneBtnEditView().click();
+ String description2 = ruleChainsPage.descriptionEntityView().getAttribute("value");
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.changeDescription("");
+ ruleChainsPage.doneBtnEditView().click();
+
+ Assert.assertTrue(ruleChainsPage.descriptionEntityView().getAttribute("value").isEmpty());
+ Assert.assertEquals(description, description1);
+ Assert.assertEquals(description + description, description2);
+ }
+
+ @Test(priority = 20, groups = "smoke")
+ @Description
+ public void debugMode() {
+ String ruleChainName = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.detailsBtn(ruleChainName).click();
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.debugCheckboxEdit().click();
+ ruleChainsPage.doneBtnEditView().click();
+ boolean debugMode = Boolean.parseBoolean(ruleChainsPage.debugCheckboxView().getAttribute("aria-checked"));
+ ruleChainsPage.editPencilBtn().click();
+ ruleChainsPage.debugCheckboxEdit().click();
+ ruleChainsPage.doneBtnEditView().click();
+
+ Assert.assertFalse(Boolean.parseBoolean(ruleChainsPage.debugCheckboxView().getAttribute("aria-checked")));
+ Assert.assertTrue(debugMode);
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java
new file mode 100644
index 0000000000..8594147c99
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SearchRuleChainTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.DataProviderCredential;
+
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class SearchRuleChainTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "ruleChainNameForSearchByFirstAndSecondWord")
+ @Description
+ public void searchFirstWord(String namePath) {
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.searchEntity(namePath);
+ ruleChainsPage.setRuleChainName(0);
+
+ Assert.assertTrue(ruleChainsPage.getRuleChainName().contains(namePath));
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSearchBySymbolAndNumber")
+ @Description
+ public void searchNumber(String name, String namePath) {
+ testRestClient.postRuleChain(defaultRuleChainPrototype(name));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.searchEntity(namePath);
+ ruleChainsPage.setRuleChainName(0);
+ boolean ruleChainContainsNamePath = ruleChainsPage.getRuleChainName().contains(namePath);
+
+ testRestClient.deleteRuleChain(getRuleChainByName(name).getId());
+
+ Assert.assertTrue(ruleChainContainsNamePath);
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java
new file mode 100644
index 0000000000..6b9de5d1d1
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByNameTest.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+import org.thingsboard.server.msa.ui.utils.DataProviderCredential;
+
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class SortByNameTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
+ @Description
+ public void specialCharacterUp(String ruleChainName) {
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.sortByNameBtn().click();
+ ruleChainsPage.setRuleChainName(0);
+
+ Assert.assertEquals(ruleChainsPage.getRuleChainName(), ruleChainName);
+ }
+
+ @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
+ @Description
+ public void allSortUp(String ruleChain, String ruleChainSymbol, String ruleChainNumber) {
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainSymbol));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainNumber));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.sortByNameBtn().click();
+ ruleChainsPage.setRuleChainName(0);
+ String firstRuleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.setRuleChainName(1);
+ String secondRuleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.setRuleChainName(2);
+ String thirdRuleChain = ruleChainsPage.getRuleChainName();
+
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChain).getId());
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainNumber).getId());
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainSymbol).getId());
+
+ Assert.assertEquals(firstRuleChain, ruleChainSymbol);
+ Assert.assertEquals(secondRuleChain, ruleChainNumber);
+ Assert.assertEquals(thirdRuleChain, ruleChain);
+ }
+
+ @Test(priority = 10, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForSort")
+ @Description
+ public void specialCharacterDown(String ruleChainName) {
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainName));
+ this.ruleChainName = ruleChainName;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.sortByNameDown();
+ ruleChainsPage.setRuleChainName(ruleChainsPage.allNames().size() - 1);
+
+ Assert.assertEquals(ruleChainsPage.getRuleChainName(), ruleChainName);
+ }
+
+ @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "nameForAllSort")
+ @Description
+ public void allSortDown(String ruleChain, String ruleChainSymbol, String ruleChainNumber) {
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainSymbol));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChainNumber));
+
+ sideBarMenuView.ruleChainsBtn().click();
+ int lastIndex = ruleChainsPage.allNames().size() - 1;
+ ruleChainsPage.sortByNameDown();
+ ruleChainsPage.setRuleChainName(lastIndex);
+ String firstRuleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.setRuleChainName(lastIndex - 1);
+ String secondRuleChain = ruleChainsPage.getRuleChainName();
+ ruleChainsPage.setRuleChainName(lastIndex - 2);
+ String thirdRuleChain = ruleChainsPage.getRuleChainName();
+
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChain).getId());
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainNumber).getId());
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainSymbol).getId());
+
+ Assert.assertEquals(firstRuleChain, ruleChainSymbol);
+ Assert.assertEquals(secondRuleChain, ruleChainNumber);
+ Assert.assertEquals(thirdRuleChain, ruleChain);
+ }
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java
new file mode 100644
index 0000000000..4d08ccc42c
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/ruleChainsSmoke/SortByTimeTest.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.tests.ruleChainsSmoke;
+
+import io.qameta.allure.Description;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest;
+import org.thingsboard.server.msa.ui.pages.LoginPageHelper;
+import org.thingsboard.server.msa.ui.pages.RuleChainsPageHelper;
+import org.thingsboard.server.msa.ui.pages.SideBarMenuViewElements;
+
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_EMAIL;
+import static org.thingsboard.server.msa.ui.utils.Const.TENANT_PASSWORD;
+import static org.thingsboard.server.msa.ui.utils.EntityPrototypes.defaultRuleChainPrototype;
+
+public class SortByTimeTest extends AbstractDriverBaseTest {
+
+ private SideBarMenuViewElements sideBarMenuView;
+ private RuleChainsPageHelper ruleChainsPage;
+ private String ruleChainName;
+
+ @BeforeMethod
+ public void login() {
+ openLocalhost();
+ new LoginPageHelper(driver).authorizationTenant();
+ testRestClient.login(TENANT_EMAIL, TENANT_PASSWORD);
+ sideBarMenuView = new SideBarMenuViewElements(driver);
+ ruleChainsPage = new RuleChainsPageHelper(driver);
+ }
+
+ @AfterMethod
+ public void delete() {
+ if (ruleChainName != null) {
+ testRestClient.deleteRuleChain(getRuleChainByName(ruleChainName).getId());
+ ruleChainName = null;
+ }
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void sortByTimeDown() {
+ String ruleChain = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
+ ruleChainName = ruleChain;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.setSort();
+ String firstListElement = ruleChainsPage.getSort().get(ruleChainsPage.getSort().size() - 1);
+ String lastCreated = ruleChainsPage.createdTime().get(0).getText();
+
+ Assert.assertEquals(firstListElement, lastCreated);
+ Assert.assertNotNull(ruleChainsPage.createdTimeEntity(ruleChain, lastCreated));
+ }
+
+ @Test(priority = 10, groups = "smoke")
+ @Description
+ public void sortByTimeUp() {
+ String ruleChain = ENTITY_NAME;
+ testRestClient.postRuleChain(defaultRuleChainPrototype(ruleChain));
+ ruleChainName = ruleChain;
+
+ sideBarMenuView.ruleChainsBtn().click();
+ ruleChainsPage.sortByTimeBtn().click();
+ ruleChainsPage.setSort();
+ String firstListElement = ruleChainsPage.getSort().get(ruleChainsPage.getSort().size() - 1);
+ String lastCreated = ruleChainsPage.createdTime().get(ruleChainsPage.createdTime().size() - 1).getText();
+
+ Assert.assertEquals(firstListElement, lastCreated);
+ Assert.assertNotNull(ruleChainsPage.createdTimeEntity(ruleChain, lastCreated));
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/Const.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/Const.java
new file mode 100644
index 0000000000..8868d53768
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/Const.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.utils;
+
+import static org.thingsboard.server.msa.TestProperties.getBaseUrl;
+import static org.thingsboard.server.msa.ui.base.AbstractBasePage.getRandomNumber;
+
+public class Const {
+
+ public static final String URL = getBaseUrl();
+ public static final String TENANT_EMAIL = "tenant@thingsboard.org";
+ public static final String TENANT_PASSWORD = "tenant";
+ public static final String ENTITY_NAME = "Az!@#$%^&*()_-+=~`" + getRandomNumber();
+ public static final String ROOT_RULE_CHAIN_NAME = "Root Rule Chain";
+ public static final String IMPORT_RULE_CHAIN_NAME = "Rule Chain from Import";
+ public static final String IMPORT_RULE_CHAIN_FILE_NAME = "forImport.json";
+ public static final String IMPORT_TXT_FILE_NAME = "forImport.txt";
+ public static final String EMPTY_IMPORT_MESSAGE = "No file selected";
+ public static final String EMPTY_RULE_CHAIN_MESSAGE = "Rule chain name should be specified!";
+ public static final String EMPTY_CUSTOMER_MESSAGE = "Customer title should be specified!";
+ public static final String DELETE_RULE_CHAIN_WITH_PROFILE_MESSAGE = "The rule chain referenced by the device profiles cannot be deleted!";
+ public static final String SAME_NAME_WARNING_CUSTOMER_MESSAGE = "Customer with such title already exists!";
+ public static final String PHONE_NUMBER_ERROR_MESSAGE = "Phone number is invalid or not possible";
+}
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java
new file mode 100644
index 0000000000..5536338fe5
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/DataProviderCredential.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.utils;
+
+import org.testng.annotations.DataProvider;
+
+import static org.thingsboard.server.msa.ui.base.AbstractBasePage.getRandomSymbol;
+import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME;
+
+public class DataProviderCredential {
+
+ private static final String SYMBOL = String.valueOf(getRandomSymbol());
+ private static final String NAME = ENTITY_NAME;
+ private static final String NUMBER = "1";
+ private static final String LONG_PHONE_NUMBER = "20155501231";
+ private static final String SHORT_PHONE_NUMBER = "201555011";
+ private static final String RULE_CHAIN_SECOND_WORD_NAME_PATH = "Rule";
+ private static final String CUSTOMER_SECOND_WORD_NAME_PATH = "Customer";
+ private static final String RULE_CHAIN_FIRST_WORD_NAME_PATH = "Root";
+ private static final String CUSTOMER_FIRST_WORD_NAME_PATH = "A";
+
+ @DataProvider
+ public static Object[][] ruleChainNameForSearchByFirstAndSecondWord() {
+ return new Object[][]{
+ {RULE_CHAIN_SECOND_WORD_NAME_PATH},
+ {RULE_CHAIN_FIRST_WORD_NAME_PATH}};
+ }
+
+ @DataProvider
+ public static Object[][] nameForSearchBySymbolAndNumber() {
+ return new Object[][]{
+ {NAME, ENTITY_NAME.split("`")[1]},
+ {NAME, String.valueOf(getRandomSymbol())}};
+ }
+
+ @DataProvider
+ public static Object[][] nameForSort() {
+ return new Object[][]{
+ {NAME},
+ {SYMBOL},
+ {NUMBER}};
+ }
+
+ @DataProvider
+ public static Object[][] nameForAllSort() {
+ return new Object[][]{
+ {NAME, SYMBOL, NUMBER}};
+ }
+
+ @DataProvider
+ public static Object[][] incorrectPhoneNumber() {
+ return new Object[][]{
+ {LONG_PHONE_NUMBER},
+ {SHORT_PHONE_NUMBER},
+ {ENTITY_NAME}};
+ }
+
+ @DataProvider
+ public static Object[][] customerNameForSearchByFirstAndSecondWord() {
+ return new Object[][]{
+ {CUSTOMER_FIRST_WORD_NAME_PATH},
+ {CUSTOMER_SECOND_WORD_NAME_PATH}};
+ }
+}
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java
new file mode 100644
index 0000000000..5fcfe8f4f9
--- /dev/null
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/utils/EntityPrototypes.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2016-2022 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.msa.ui.utils;
+
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.rule.RuleChain;
+
+public class EntityPrototypes {
+
+ public static Customer defaultCustomerPrototype(String entityName){
+ Customer customer = new Customer();
+ customer.setTitle(entityName);
+ return customer;
+ }
+
+ public static RuleChain defaultRuleChainPrototype(String entityName){
+ RuleChain ruleChain = new RuleChain();
+ ruleChain.setName(entityName);
+ return ruleChain;
+ }
+}
diff --git a/msa/black-box-tests/src/test/resources/all.xml b/msa/black-box-tests/src/test/resources/all.xml
new file mode 100644
index 0000000000..fe8b908a86
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/all.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/resources/allure.properties b/msa/black-box-tests/src/test/resources/allure.properties
new file mode 100644
index 0000000000..f0525ba437
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/allure.properties
@@ -0,0 +1,2 @@
+allure.results.directory=target/allure-results
+allure.output.directory=target/allure-results
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/resources/testNG.xml b/msa/black-box-tests/src/test/resources/connectivity.xml
similarity index 95%
rename from msa/black-box-tests/src/test/resources/testNG.xml
rename to msa/black-box-tests/src/test/resources/connectivity.xml
index 45e93f76f1..c2c12c4efb 100644
--- a/msa/black-box-tests/src/test/resources/testNG.xml
+++ b/msa/black-box-tests/src/test/resources/connectivity.xml
@@ -18,7 +18,7 @@
-->
-
+
diff --git a/msa/black-box-tests/src/test/resources/docker-selenium.yml b/msa/black-box-tests/src/test/resources/docker-selenium.yml
new file mode 100644
index 0000000000..83b5462a82
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/docker-selenium.yml
@@ -0,0 +1,37 @@
+#
+# Copyright © 2016-2022 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+version: '3'
+services:
+ selenium-chrome:
+ restart: always
+ image: selenium/standalone-chrome
+ ports:
+ - '4444:4444'
+ - '7900:7900'
+ shm_size: 2gb
+ environment:
+ SE_NODE_MAX_SESSIONS: 8
+ SE_NODE_OVERRIDE_MAX_SESSIONS: 'true'
+ SE_NODE_SESSION_TIMEOUT: 5000
+ SE_SCREEN_WIDTH: 1920
+ SE_SCREEN_HEIGHT: 1080
+ SE_SCREEN_DEPTH: 24
+ SE_SCREEN_DPI: 74
+ extra_hosts:
+ - "host.docker.internal:172.17.0.1"
+
+
diff --git a/msa/black-box-tests/src/test/resources/forImport.json b/msa/black-box-tests/src/test/resources/forImport.json
new file mode 100644
index 0000000000..40367d7609
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/forImport.json
@@ -0,0 +1,20 @@
+{
+ "ruleChain": {
+ "additionalInfo": {
+ "description": ""
+ },
+ "name": "Rule Chain from Import",
+ "type": "CORE",
+ "firstRuleNodeId": null,
+ "root": false,
+ "debugMode": false,
+ "configuration": null,
+ "externalId": null
+ },
+ "metadata": {
+ "firstNodeIndex": null,
+ "nodes": [],
+ "connections": null,
+ "ruleChainConnections": null
+ }
+}
diff --git a/msa/black-box-tests/src/test/resources/forImport.txt b/msa/black-box-tests/src/test/resources/forImport.txt
new file mode 100644
index 0000000000..908a557812
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/forImport.txt
@@ -0,0 +1,16 @@
+====
+ Copyright © 2016-2022 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+====
+
diff --git a/msa/black-box-tests/src/test/resources/smokesCustomer.xml b/msa/black-box-tests/src/test/resources/smokesCustomer.xml
new file mode 100644
index 0000000000..5ac515c0fe
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/smokesCustomer.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/resources/smokesRuleChain.xml b/msa/black-box-tests/src/test/resources/smokesRuleChain.xml
new file mode 100644
index 0000000000..5834ad6558
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/smokesRuleChain.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/black-box-tests/src/test/resources/uiTests.xml b/msa/black-box-tests/src/test/resources/uiTests.xml
new file mode 100644
index 0000000000..bd163de930
--- /dev/null
+++ b/msa/black-box-tests/src/test/resources/uiTests.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json
index e094dcc4e5..f9fde3328d 100644
--- a/msa/js-executor/package.json
+++ b/msa/js-executor/package.json
@@ -1,7 +1,7 @@
{
"name": "thingsboard-js-executor",
"private": true,
- "version": "3.4.3",
+ "version": "3.4.4",
"description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.ts",
"bin": "server.js",
diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml
index 860eb3891a..76edeee65c 100644
--- a/msa/js-executor/pom.xml
+++ b/msa/js-executor/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/pom.xml b/msa/pom.xml
index 9e628b0f70..2542e0d1d0 100644
--- a/msa/pom.xml
+++ b/msa/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
msa
diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml
index 7119da74d8..bb53c84a2f 100644
--- a/msa/tb-node/pom.xml
+++ b/msa/tb-node/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml
index fe79951126..b717881833 100644
--- a/msa/tb/pom.xml
+++ b/msa/tb/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml
index 82d7e02da6..80f316318a 100644
--- a/msa/transport/coap/pom.xml
+++ b/msa/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml
index a596c5000f..a5e2a7d73d 100644
--- a/msa/transport/http/pom.xml
+++ b/msa/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml
index 0a43e7deb5..01a9fc5628 100644
--- a/msa/transport/lwm2m/pom.xml
+++ b/msa/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml
index 2fe358fe5d..82856357f6 100644
--- a/msa/transport/mqtt/pom.xml
+++ b/msa/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.msa
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.msa.transport
diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml
index 3339d93577..4df10d1b46 100644
--- a/msa/transport/pom.xml
+++ b/msa/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml
index 1a352f47fb..f6cb54dfd2 100644
--- a/msa/transport/snmp/pom.xml
+++ b/msa/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.msa
transport
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
org.thingsboard.msa.transport
diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml
index 05884931d3..fbded58c33 100644
--- a/msa/vc-executor-docker/pom.xml
+++ b/msa/vc-executor-docker/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml
index 31c96c583c..9b6050e029 100644
--- a/msa/vc-executor/pom.xml
+++ b/msa/vc-executor/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json
index 035b8a3072..868215ad08 100644
--- a/msa/web-ui/package.json
+++ b/msa/web-ui/package.json
@@ -1,7 +1,7 @@
{
"name": "thingsboard-web-ui",
"private": true,
- "version": "3.4.3",
+ "version": "3.4.4",
"description": "ThingsBoard Web UI Microservice",
"main": "server.ts",
"bin": "server.js",
diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml
index 5a0a444687..fe94b5bd40 100644
--- a/msa/web-ui/pom.xml
+++ b/msa/web-ui/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
msa
org.thingsboard.msa
diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml
index 99d05fc583..fdc53f6b36 100644
--- a/netty-mqtt/pom.xml
+++ b/netty-mqtt/pom.xml
@@ -19,11 +19,11 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
netty-mqtt
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
jar
Netty MQTT Client
diff --git a/pom.xml b/pom.xml
index 568363c703..df1cf68006 100755
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
pom
Thingsboard
@@ -54,7 +54,7 @@
4.15.0
4.0.5
3.11.14
- 30.0-jre
+ 31.1-jre
2.6.1
3.4
1.15
@@ -144,6 +144,10 @@
6.1.0.202203080745-r
0.4.8
1.0.0
+ 4.6.0
+ 5.2.0
+ 2.19.0
+ 2.8
@@ -1656,6 +1660,30 @@
${rest-assured.version}
test
+
+ org.seleniumhq.selenium
+ selenium-java
+ ${selenium.version}
+ test
+
+
+ io.github.bonigarcia
+ webdrivermanager
+ ${webdrivermanager.version}
+ test
+
+
+ io.qameta.allure
+ allure-testng
+ ${allure-testng.version}
+ test
+
+
+ io.qameta.allure
+ allure-maven
+ ${allure-maven.version}
+ test
+
org.hamcrest
hamcrest-all
diff --git a/rest-client/pom.xml b/rest-client/pom.xml
index 24f8453786..943466a411 100644
--- a/rest-client/pom.xml
+++ b/rest-client/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
rest-client
diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml
index 126885cd22..fc27ede39e 100644
--- a/rule-engine/pom.xml
+++ b/rule-engine/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
rule-engine
diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml
index 665158a44f..c696866004 100644
--- a/rule-engine/rule-engine-api/pom.xml
+++ b/rule-engine/rule-engine-api/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
rule-engine
org.thingsboard.rule-engine
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index d32d8163a6..0c92fe9672 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
rule-engine
org.thingsboard.rule-engine
diff --git a/tools/pom.xml b/tools/pom.xml
index d414a7a777..de23ae355d 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
tools
diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml
index 2640d1d80d..64afdbfd66 100644
--- a/transport/coap/pom.xml
+++ b/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/http/pom.xml b/transport/http/pom.xml
index 7820c585ec..4f7ad8cd01 100644
--- a/transport/http/pom.xml
+++ b/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml
index b36c8372ef..e6970f8ac1 100644
--- a/transport/lwm2m/pom.xml
+++ b/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml
index 91ce12908b..1dd97ec2ea 100644
--- a/transport/mqtt/pom.xml
+++ b/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
org.thingsboard.transport
diff --git a/transport/pom.xml b/transport/pom.xml
index 26af07f6a2..be6eceaec1 100644
--- a/transport/pom.xml
+++ b/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
transport
diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml
index 0701f7aa57..6f9acc0d7c 100644
--- a/transport/snmp/pom.xml
+++ b/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
transport
diff --git a/ui-ngx/package.json b/ui-ngx/package.json
index 6b8112adad..3edaefcf35 100644
--- a/ui-ngx/package.json
+++ b/ui-ngx/package.json
@@ -1,6 +1,6 @@
{
"name": "thingsboard",
- "version": "3.4.3",
+ "version": "3.4.4",
"scripts": {
"ng": "ng",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",
diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml
index f78d168144..a2640113e3 100644
--- a/ui-ngx/pom.xml
+++ b/ui-ngx/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.4.3-SNAPSHOT
+ 3.4.4-SNAPSHOT
thingsboard
org.thingsboard
diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts
index b66399de6c..035381af69 100644
--- a/ui-ngx/src/app/core/http/entity.service.ts
+++ b/ui-ngx/src/app/core/http/entity.service.ts
@@ -869,8 +869,26 @@ export class EntityService {
};
aliasInfo.currentEntity = null;
if (!aliasInfo.resolveMultiple && aliasInfo.entityFilter) {
- return this.findSingleEntityInfoByEntityFilter(aliasInfo.entityFilter,
- {ignoreLoading: true, ignoreErrors: true}).pipe(
+ let currentEntity: EntityInfo = null;
+ if (result.stateEntity && aliasInfo.entityFilter.type === AliasFilterType.singleEntity) {
+ if (stateParams) {
+ let targetParams = stateParams;
+ if (result.entityParamName && result.entityParamName.length) {
+ targetParams = stateParams[result.entityParamName];
+ }
+ if (targetParams && targetParams.entityId) {
+ currentEntity = {
+ id: targetParams.entityId.id,
+ entityType: targetParams.entityId.entityType as EntityType,
+ name: targetParams.entityName,
+ label: targetParams.entityLabel
+ };
+ }
+ }
+ }
+ const entityInfoObservable = currentEntity ? of(currentEntity) : this.findSingleEntityInfoByEntityFilter(aliasInfo.entityFilter,
+ {ignoreLoading: true, ignoreErrors: true});
+ return entityInfoObservable.pipe(
map((entity) => {
aliasInfo.currentEntity = entity;
return aliasInfo;
diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts
index 31788122a9..dd9449032e 100644
--- a/ui-ngx/src/app/core/http/widget.service.ts
+++ b/ui-ngx/src/app/core/http/widget.service.ts
@@ -16,7 +16,7 @@
import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
-import { Observable, of, ReplaySubject, Subject } from 'rxjs';
+import { Observable, of, ReplaySubject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data';
@@ -43,15 +43,14 @@ import { ActivationEnd, Router } from '@angular/router';
})
export class WidgetService {
- private widgetTypeUpdatedSubject = new Subject();
- private widgetsBundleDeletedSubject = new Subject();
-
private allWidgetsBundles: Array;
private systemWidgetsBundles: Array;
private tenantWidgetsBundles: Array;
private widgetTypeInfosCache = new Map>();
+ private widgetsInfoInMemoryCache = new Map();
+
private loadWidgetsBundleCacheSubject: ReplaySubject;
constructor(
@@ -117,7 +116,7 @@ export class WidgetService {
defaultHttpOptionsFromConfig(config)).pipe(
tap(() => {
this.invalidateWidgetsBundleCache();
- this.widgetsBundleDeletedSubject.next(widgetsBundle);
+ this.widgetsBundleDeleted(widgetsBundle);
})
);
}
@@ -217,7 +216,7 @@ export class WidgetService {
return this.http.post('/api/widgetType', widgetTypeDetails,
defaultHttpOptionsFromConfig(config)).pipe(
tap((savedWidgetType) => {
- this.widgetTypeUpdatedSubject.next(savedWidgetType);
+ this.widgetTypeUpdated(savedWidgetType);
}));
}
@@ -226,7 +225,7 @@ export class WidgetService {
return this.http.post('/api/widgetType', widgetTypeDetails,
defaultHttpOptionsFromConfig(config)).pipe(
tap((savedWidgetType) => {
- this.widgetTypeUpdatedSubject.next(savedWidgetType);
+ this.widgetTypeUpdated(savedWidgetType);
}));
}
@@ -237,7 +236,7 @@ export class WidgetService {
return this.http.delete(`/api/widgetType/${widgetTypeInstance.id.id}`,
defaultHttpOptionsFromConfig(config)).pipe(
tap(() => {
- this.widgetTypeUpdatedSubject.next(widgetTypeInstance);
+ this.widgetTypeUpdated(widgetTypeInstance);
})
);
}
@@ -263,12 +262,40 @@ export class WidgetService {
);
}
- public onWidgetTypeUpdated(): Observable {
- return this.widgetTypeUpdatedSubject.asObservable();
+ public createWidgetInfoCacheKey(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): string {
+ return `${isSystem ? 'sys_' : ''}${bundleAlias}_${widgetTypeAlias}`;
+ }
+
+ public getWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): WidgetInfo | undefined {
+ const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
+ return this.widgetsInfoInMemoryCache.get(key);
}
- public onWidgetBundleDeleted(): Observable {
- return this.widgetsBundleDeletedSubject.asObservable();
+ public putWidgetInfoToCache(widgetInfo: WidgetInfo, bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) {
+ const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
+ this.widgetsInfoInMemoryCache.set(key, widgetInfo);
+ }
+
+ private widgetTypeUpdated(updatedWidgetType: WidgetType): void {
+ this.deleteWidgetInfoFromCache(updatedWidgetType.bundleAlias, updatedWidgetType.alias, updatedWidgetType.tenantId.id === NULL_UUID);
+ }
+
+ private widgetsBundleDeleted(widgetsBundle: WidgetsBundle): void {
+ this.deleteWidgetsBundleFromCache(widgetsBundle.alias, widgetsBundle.tenantId.id === NULL_UUID);
+ }
+
+ private deleteWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) {
+ const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
+ this.widgetsInfoInMemoryCache.delete(key);
+ }
+
+ private deleteWidgetsBundleFromCache(bundleAlias: string, isSystem: boolean) {
+ const key = (isSystem ? 'sys_' : '') + bundleAlias;
+ this.widgetsInfoInMemoryCache.forEach((widgetInfo, cacheKey) => {
+ if (cacheKey.startsWith(key)) {
+ this.widgetsInfoInMemoryCache.delete(cacheKey);
+ }
+ });
}
private loadWidgetsBundleCache(config?: RequestConfig): Observable {
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
index ca1bae3de7..4c641ad1d7 100644
--- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
+++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
@@ -135,7 +135,7 @@
(click)="openDashboardSettings($event)">
settings
-
+ [ngStyle]="{width: mainLayoutSize.width,
+ height: mainLayoutSize.height}">
this.dashboard,
@@ -402,6 +410,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
.observe(MediaBreakpoints['gt-sm'])
.subscribe((state: BreakpointState) => {
this.isMobile = !state.matches;
+ this.updateLayoutSizes();
}
));
if (this.isMobileApp && this.syncStateWithQueryParam) {
@@ -414,6 +423,13 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
}
+ ngAfterViewInit() {
+ this.dashboardResize$ = new ResizeObserver(() => {
+ this.updateLayoutSizes();
+ });
+ this.dashboardResize$.observe(this.dashboardContainer.nativeElement);
+ }
+
private init(data: DashboardPageInitData) {
this.reset();
@@ -537,6 +553,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
subscription.unsubscribe();
});
this.rxSubscriptions.length = 0;
+ if (this.dashboardResize$) {
+ this.dashboardResize$.disconnect();
+ }
}
public runChangeDetection() {
@@ -679,28 +698,48 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened);
}
- public mainLayoutWidth(): string {
- if (this.isEditingWidget && this.editingLayoutCtx.id === 'main') {
- return '100%';
- } else {
- return this.layouts.right.show && !this.isMobile ? this.calculateWidth('main') : '100%';
+ private updateLayoutSizes() {
+ let changeMainLayoutSize = false;
+ let changeRightLayoutSize = false;
+ if (this.dashboardCtx.state) {
+ changeMainLayoutSize = this.updateMainLayoutSize();
+ changeRightLayoutSize = this.updateRightLayoutSize();
+ }
+ if (changeMainLayoutSize || changeRightLayoutSize) {
+ this.cd.markForCheck();
}
}
- public mainLayoutHeight(): string {
+ private updateMainLayoutSize(): boolean {
+ const prevMainLayoutWidth = this.mainLayoutSize.width;
+ const prevMainLayoutHeight = this.mainLayoutSize.height;
+ if (this.isEditingWidget && this.editingLayoutCtx.id === 'main') {
+ this.mainLayoutSize.width = '100%';
+ } else {
+ this.mainLayoutSize.width = this.layouts.right.show && !this.isMobile ? this.calculateWidth('main') : '100%';
+ }
if (!this.isEditingWidget || this.editingLayoutCtx.id === 'main') {
- return '100%';
+ this.mainLayoutSize.height = '100%';
} else {
- return '0px';
+ this.mainLayoutSize.height = '0px';
}
+ return prevMainLayoutWidth !== this.mainLayoutSize.width || prevMainLayoutHeight !== this.mainLayoutSize.height;
}
- public rightLayoutWidth(): string {
+ private updateRightLayoutSize(): boolean {
+ const prevRightLayoutWidth = this.rightLayoutSize.width;
+ const prevRightLayoutHeight = this.rightLayoutSize.height;
if (this.isEditingWidget && this.editingLayoutCtx.id === 'right') {
- return '100%';
+ this.rightLayoutSize.width = '100%';
+ } else {
+ this.rightLayoutSize.width = this.isMobile ? '100%' : this.calculateWidth('right');
+ }
+ if (!this.isEditingWidget || this.editingLayoutCtx.id === 'right') {
+ this.rightLayoutSize.height = '100%';
} else {
- return this.isMobile ? '100%' : this.calculateWidth('right');
+ this.rightLayoutSize.height = '0px';
}
+ return prevRightLayoutWidth !== this.rightLayoutSize.width || prevRightLayoutHeight !== this.rightLayoutSize.height;
}
private calculateWidth(layout: DashboardLayoutId): string {
@@ -743,14 +782,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
}
- public rightLayoutHeight(): string {
- if (!this.isEditingWidget || this.editingLayoutCtx.id === 'right') {
- return '100%';
- } else {
- return '0px';
- }
- }
-
public isPublicUser(): boolean {
return this.authUser.isPublic;
}
@@ -977,6 +1008,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
layout.layoutCtx.ctrl.reload();
}
layout.layoutCtx.ignoreLoading = true;
+ this.updateLayoutSizes();
}
private setEditMode(isEdit: boolean, revert: boolean) {
@@ -1191,6 +1223,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.editingLayoutCtx = null;
this.editingWidgetSubtitle = null;
this.isEditingWidget = false;
+ this.updateLayoutSizes();
this.resetHighlight();
this.forceDashboardMobileMode = false;
}
@@ -1216,6 +1249,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.editingWidgetSubtitle = this.widgetComponentService.getInstantWidgetInfo(this.editingWidget).widgetName;
this.forceDashboardMobileMode = true;
this.isEditingWidget = true;
+ this.updateLayoutSizes();
if (layoutCtx) {
const delayOffset = transition ? 350 : 0;
const delay = transition ? 400 : 300;
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.ts
index 4d593a7c57..801238d602 100644
--- a/ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/components/dashboard-page/layout/manage-dashboard-layouts-dialog.component.ts
@@ -297,6 +297,7 @@ export class ManageDashboardLayoutsDialogComponent extends DialogComponent();
-
private widgetsInfoFetchQueue = new Map>>();
private init$: Observable;
@@ -77,14 +74,6 @@ export class WidgetComponentService {
this.cssParser.testMode = false;
- this.widgetService.onWidgetTypeUpdated().subscribe((widgetType) => {
- this.deleteWidgetInfoFromCache(widgetType.bundleAlias, widgetType.alias, widgetType.tenantId.id === NULL_UUID);
- });
-
- this.widgetService.onWidgetBundleDeleted().subscribe((widgetsBundle) => {
- this.deleteWidgetsBundleFromCache(widgetsBundle.alias, widgetsBundle.tenantId.id === NULL_UUID);
- });
-
this.init();
}
@@ -223,7 +212,7 @@ export class WidgetComponentService {
}
public getInstantWidgetInfo(widget: Widget): WidgetInfo {
- const widgetInfo = this.getWidgetInfoFromCache(widget.bundleAlias, widget.typeAlias, widget.isSystemType);
+ const widgetInfo = this.widgetService.getWidgetInfoFromCache(widget.bundleAlias, widget.typeAlias, widget.isSystemType);
if (widgetInfo) {
return widgetInfo;
} else {
@@ -239,7 +228,7 @@ export class WidgetComponentService {
private getWidgetInfoInternal(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): Observable {
const widgetInfoSubject = new ReplaySubject();
- const widgetInfo = this.getWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem);
+ const widgetInfo = this.widgetService.getWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem);
if (widgetInfo) {
widgetInfoSubject.next(widgetInfo);
widgetInfoSubject.complete();
@@ -247,7 +236,7 @@ export class WidgetComponentService {
if (this.utils.widgetEditMode) {
this.loadWidget(this.editingWidgetType, bundleAlias, isSystem, widgetInfoSubject);
} else {
- const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
+ const key = this.widgetService.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
let fetchQueue = this.widgetsInfoFetchQueue.get(key);
if (fetchQueue) {
fetchQueue.push(widgetInfoSubject);
@@ -272,7 +261,7 @@ export class WidgetComponentService {
private loadWidget(widgetType: WidgetType, bundleAlias: string, isSystem: boolean, widgetInfoSubject: Subject) {
const widgetInfo = toWidgetInfo(widgetType);
- const key = this.createWidgetInfoCacheKey(bundleAlias, widgetInfo.alias, isSystem);
+ const key = this.widgetService.createWidgetInfoCacheKey(bundleAlias, widgetInfo.alias, isSystem);
let widgetControllerDescriptor: WidgetControllerDescriptor = null;
try {
widgetControllerDescriptor = this.createWidgetControllerDescriptor(widgetInfo, key);
@@ -297,7 +286,7 @@ export class WidgetComponentService {
widgetInfo.typeParameters = widgetControllerDescriptor.typeParameters;
widgetInfo.actionSources = widgetControllerDescriptor.actionSources;
widgetInfo.widgetTypeFunction = widgetControllerDescriptor.widgetTypeFunction;
- this.putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem);
+ this.widgetService.putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem);
if (widgetInfoSubject) {
widgetInfoSubject.next(widgetInfo);
widgetInfoSubject.complete();
@@ -331,7 +320,7 @@ export class WidgetComponentService {
(resource) => {
resourceTasks.push(
this.resources.loadResource(resource.url).pipe(
- catchError(e => of(`Failed to load widget resource: '${resource.url}'`))
+ catchError(() => of(`Failed to load widget resource: '${resource.url}'`))
)
);
}
@@ -586,34 +575,4 @@ export class WidgetComponentService {
this.widgetsInfoFetchQueue.delete(key);
}
}
-
- // Cache functions
-
- private createWidgetInfoCacheKey(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): string {
- return `${isSystem ? 'sys_' : ''}${bundleAlias}_${widgetTypeAlias}`;
- }
-
- private getWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): WidgetInfo | undefined {
- const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
- return this.widgetsInfoInMemoryCache.get(key);
- }
-
- private putWidgetInfoToCache(widgetInfo: WidgetInfo, bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) {
- const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
- this.widgetsInfoInMemoryCache.set(key, widgetInfo);
- }
-
- private deleteWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) {
- const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem);
- this.widgetsInfoInMemoryCache.delete(key);
- }
-
- private deleteWidgetsBundleFromCache(bundleAlias: string, isSystem: boolean) {
- const key = (isSystem ? 'sys_' : '') + bundleAlias;
- this.widgetsInfoInMemoryCache.forEach((widgetInfo, cacheKey) => {
- if (cacheKey.startsWith(key)) {
- this.widgetsInfoInMemoryCache.delete(cacheKey);
- }
- });
- }
}