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 76ce467fd9..880143b58a 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 @@ -132,6 +132,18 @@ public class TestRestClient { .as(Device.class); } + public PageData getDevices(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return given().spec(requestSpec).queryParams(params) + .get("/api/tenant/devices") + .then() + .statusCode(HTTP_OK) + .extract() + .as(new TypeRef>() { + }); + } + public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) { return given().spec(requestSpec).get("/api/device/{deviceId}/credentials", deviceId.getId()) .then() 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 index cd34e69239..eb6776a4e4 100644 --- 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 @@ -37,6 +37,7 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; 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.asset.AssetProfile; import org.thingsboard.server.common.data.id.AlarmId; @@ -150,6 +151,12 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest { .findFirst().orElse(null); } + public Device getDeviceByName(String name) { + return testRestClient.getDevices(pageLink).getData().stream() + .filter(s -> s.getName().equals(name)) + .findFirst().orElse(null); + } + public List getRuleChainsByName(String name) { return testRestClient.getRuleChains(pageLink).getData().stream() .filter(s -> s.getName().equals(name)) @@ -269,4 +276,11 @@ abstract public class AbstractDriverBaseTest extends AbstractContainerTest { testRestClient.deleteDashboard(dashboardId); } } + + public void deleteDeviceByName(String deviceName) { + Device device = getDeviceByName(deviceName); + if (device != null) { + testRestClient.deleteDevice(device.getId()); + } + } } 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 index 25b3aff7e1..ee89e5053d 100644 --- 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 @@ -119,6 +119,7 @@ public class CustomerPageHelper extends CustomerPageElements { public void createCustomersUser() { plusBtn().click(); + addUserEmailField().click(); addUserEmailField().sendKeys(getRandomNumber() + "@gmail.com"); addBtnC().click(); activateWindowOkBtn().click(); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java index b8fbda5135..030f7913c1 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageElements.java @@ -18,7 +18,7 @@ package org.thingsboard.server.msa.ui.pages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -public class DevicePageElements extends OtherPageElements { +public class DevicePageElements extends OtherPageElementsHelper { public DevicePageElements(WebDriver driver) { super(driver); } @@ -29,9 +29,30 @@ public class DevicePageElements extends OtherPageElements { private static final String ASSIGN_TO_CUSTOMER_BTN = "//mat-cell[contains(@class,'name')]/span[text()='%s']" + "/ancestor::mat-row//mat-icon[contains(text(),'assignment_ind')]/parent::button"; private static final String CHOOSE_CUSTOMER_FOR_ASSIGN_FIELD = "//input[@formcontrolname='entity']"; - private static final String CUSTOMER_FROM_ASSIGN_DROPDOWN = "//div[@role = 'listbox']//span[text() = '%s']"; + private static final String ENTITY_FROM_DROPDOWN = "//div[@role = 'listbox']//span[text() = '%s']"; private static final String CLOSE_DEVICE_DETAILS_VIEW = "//header//mat-icon[contains(text(),'close')]/parent::button"; private static final String SUBMIT_ASSIGN_TO_CUSTOMER_BTN = "//button[@type='submit']"; + private static final String ADD_DEVICE_BTN = "//mat-icon[text() = 'insert_drive_file']/parent::button"; + private static final String CREATE_DEVICE_NAME_FIELD = "//tb-device-wizard//input[@formcontrolname='name']"; + private static final String HEADER_NAME_VIEW = "//header//div[@class='tb-details-title']/span"; + private static final String DESCRIPTION_FIELD_CREATE_VIEW = "//tb-device-wizard//textarea[@formcontrolname='description']"; + private static final String ADD_DEVICE_VIEW = "//tb-device-wizard"; + private static final String DELETE_BTN_DETAILS_TAB = "//span[contains(text(),'Delete device')]/parent::button"; + private static final String CHECKBOX_GATEWAY_EDIT = "//mat-checkbox[@formcontrolname='gateway']//label"; + private static final String CHECKBOX_GATEWAY_CREATE = "//tb-device-wizard//mat-checkbox[@formcontrolname='gateway']//label"; + private static final String CHECKBOX_OVERWRITE_ACTIVITY_TIME_EDIT = "//mat-checkbox[@formcontrolname='overwriteActivityTime']//label"; + private static final String CHECKBOX_OVERWRITE_ACTIVITY_TIME_CREATE = "//tb-device-wizard//mat-checkbox[@formcontrolname='overwriteActivityTime']//label"; + private static final String CHECKBOX_GATEWAY_DETAILS = "//mat-checkbox[@formcontrolname='gateway']//input"; + private static final String CHECKBOX_GATEWAY_PAGE = DEVICE + "/ancestor::mat-row//mat-cell[contains(@class,'cdk-column-gateway')]//mat-icon[text() = 'check_box']"; + private static final String CHECKBOX_OVERWRITE_ACTIVITY_TIME_DETAILS = "//mat-checkbox[@formcontrolname='overwriteActivityTime']//input"; + private static final String CLEAR_PROFILE_FIELD_BTN = "//button[@aria-label='Clear']"; + private static final String DEVICE_PROFILE_REDIRECTED_BTN = "//a[@aria-label='Open device profile']"; + private static final String DEVICE_LABEL_FIELD_CREATE = "//tb-device-wizard//input[@formcontrolname='label']"; + private static final String DEVICE_LABEL_PAGE = DEVICE + "/ancestor::mat-row//mat-cell[contains(@class,'cdk-column-label')]/span"; + private static final String DEVICE_CUSTOMER_PAGE = DEVICE + "/ancestor::mat-row//mat-cell[contains(@class,'cdk-column-customerTitle')]/span"; + private static final String CUSTOMER_OPTION_BNT = "//div[text() = 'Customer']/ancestor::mat-step-header"; + private static final String ASSIGN_ON_CUSTOMER_FIELD = "//input[@formcontrolname='entity']"; + private static final String DEVICE_LABEL_EDIT = "//input[@formcontrolname='label']"; public WebElement device(String deviceName) { return waitUntilElementToBeClickable(String.format(DEVICE, deviceName)); @@ -53,8 +74,8 @@ public class DevicePageElements extends OtherPageElements { return waitUntilElementToBeClickable(CHOOSE_CUSTOMER_FOR_ASSIGN_FIELD); } - public WebElement customerFromAssignDropdown(String customerTitle) { - return waitUntilElementToBeClickable(String.format(CUSTOMER_FROM_ASSIGN_DROPDOWN, customerTitle)); + public WebElement entityFromDropdown(String customerTitle) { + return waitUntilElementToBeClickable(String.format(ENTITY_FROM_DROPDOWN, customerTitle)); } public WebElement closeDeviceDetailsViewBtn() { @@ -64,4 +85,92 @@ public class DevicePageElements extends OtherPageElements { public WebElement submitAssignToCustomerBtn() { return waitUntilElementToBeClickable(SUBMIT_ASSIGN_TO_CUSTOMER_BTN); } + + public WebElement addDeviceBtn() { + return waitUntilElementToBeClickable(ADD_DEVICE_BTN); + } + + public WebElement nameField() { + return waitUntilElementToBeClickable(CREATE_DEVICE_NAME_FIELD); + } + + public WebElement headerNameView() { + return waitUntilVisibilityOfElementLocated(HEADER_NAME_VIEW); + } + + public WebElement descriptionFieldCreateField() { + return waitUntilElementToBeClickable(DESCRIPTION_FIELD_CREATE_VIEW); + } + + public WebElement addDeviceView() { + return waitUntilPresenceOfElementLocated(ADD_DEVICE_VIEW); + } + + public WebElement deleteBtnDetailsTab() { + return waitUntilElementToBeClickable(DELETE_BTN_DETAILS_TAB); + } + + public WebElement checkboxGatewayEdit() { + return waitUntilElementToBeClickable(CHECKBOX_GATEWAY_EDIT); + } + + public WebElement checkboxGatewayCreate() { + return waitUntilElementToBeClickable(CHECKBOX_GATEWAY_CREATE); + } + + public WebElement checkboxOverwriteActivityTimeEdit() { + return waitUntilElementToBeClickable(CHECKBOX_OVERWRITE_ACTIVITY_TIME_EDIT); + } + + public WebElement checkboxOverwriteActivityTimeCreate() { + return waitUntilElementToBeClickable(CHECKBOX_OVERWRITE_ACTIVITY_TIME_CREATE); + } + + public WebElement checkboxGatewayDetailsTab() { + return waitUntilPresenceOfElementLocated(CHECKBOX_GATEWAY_DETAILS); + } + + public WebElement checkboxGatewayPage(String deviceName) { + return waitUntilPresenceOfElementLocated(String.format(CHECKBOX_GATEWAY_PAGE, deviceName)); + } + + public WebElement checkboxOverwriteActivityTimeDetails() { + return waitUntilPresenceOfElementLocated(CHECKBOX_OVERWRITE_ACTIVITY_TIME_DETAILS); + } + + public WebElement clearProfileFieldBtn() { + return waitUntilElementToBeClickable(CLEAR_PROFILE_FIELD_BTN); + } + + public WebElement deviceProfileRedirectedBtn() { + return waitUntilElementToBeClickable(DEVICE_PROFILE_REDIRECTED_BTN); + } + + public WebElement deviceLabelFieldCreate() { + return waitUntilElementToBeClickable(DEVICE_LABEL_FIELD_CREATE); + } + + public WebElement deviceLabelOnPage(String deviceName) { + return waitUntilVisibilityOfElementLocated(String.format(DEVICE_LABEL_PAGE, deviceName)); + } + + public WebElement customerOptionBtn() { + return waitUntilElementToBeClickable(CUSTOMER_OPTION_BNT); + } + + public WebElement assignOnCustomerField() { + return waitUntilElementToBeClickable(ASSIGN_ON_CUSTOMER_FIELD); + } + + public WebElement deviceCustomerOnPage(String deviceName) { + return waitUntilVisibilityOfElementLocated(String.format(DEVICE_CUSTOMER_PAGE, deviceName)); + } + + public WebElement deviceLabelEditField() { + return waitUntilElementToBeClickable(DEVICE_LABEL_EDIT); + } + + public WebElement deviceLabelDetailsField() { + return waitUntilVisibilityOfElementLocated(DEVICE_LABEL_EDIT); + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java index 19e85244cd..c6df90e0fa 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/pages/DevicePageHelper.java @@ -22,6 +22,9 @@ public class DevicePageHelper extends DevicePageElements { super(driver); } + private String description; + private String label; + public void openDeviceAlarms(String deviceName) { if (!deviceDetailsView().isDisplayed()) { device(deviceName).click(); @@ -31,7 +34,63 @@ public class DevicePageHelper extends DevicePageElements { public void assignToCustomer(String customerTitle) { chooseCustomerForAssignField().click(); - customerFromAssignDropdown(customerTitle).click(); + entityFromDropdown(customerTitle).click(); submitAssignToCustomerBtn().click(); } + + public void openCreateDeviceView() { + plusBtn().click(); + addDeviceBtn().click(); + } + + public void enterName(String deviceName) { + enterText(nameField(), deviceName); + } + + public void enterDescription(String description) { + enterText(descriptionFieldCreateField(), description); + } + + public void enterLabel(String label) { + enterText(deviceLabelFieldCreate(), label); + } + + public void deleteDeviceByRightSideBtn(String deviceName) { + deleteBtn(deviceName).click(); + warningPopUpYesBtn().click(); + } + + public void deleteDeviceFromDetailsTab() { + deleteBtnDetailsTab().click(); + warningPopUpYesBtn().click(); + } + + public void setDescription() { + scrollToElement(descriptionEntityView()); + description = descriptionEntityView().getAttribute("value"); + } + + public void setLabel() { + label = deviceLabelDetailsField().getAttribute("value"); + } + + public String getDescription() { + return description; + } + + public String getLabel() { + return label; + } + + public void changeDeviceProfile(String deviceProfileName) { + clearProfileFieldBtn().click(); + entityFromDropdown(deviceProfileName).click(); + } + + public void assignOnCustomer(String customerTitle) { + customerOptionBtn().click(); + assignOnCustomerField().click(); + entityFromList(customerTitle).click(); + sleep(2); //waiting for the action to count + } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/AbstractDeviceTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/AbstractDeviceTest.java new file mode 100644 index 0000000000..3cd46fefc9 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/AbstractDeviceTest.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 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.devicessmoke; + +import io.qameta.allure.Epic; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.thingsboard.server.msa.ui.base.AbstractDriverBaseTest; +import org.thingsboard.server.msa.ui.pages.DevicePageHelper; +import org.thingsboard.server.msa.ui.pages.LoginPageHelper; +import org.thingsboard.server.msa.ui.pages.SideBarMenuViewHelper; + +@Epic("Device smoke tests") +abstract public class AbstractDeviceTest extends AbstractDriverBaseTest { + + protected SideBarMenuViewHelper sideBarMenuView; + protected DevicePageHelper devicePage; + protected String deviceName; + + @BeforeClass + public void login() { + new LoginPageHelper(driver).authorizationTenant(); + sideBarMenuView = new SideBarMenuViewHelper(driver); + devicePage = new DevicePageHelper(driver); + } + + @AfterMethod + public void delete() { + deleteDeviceByName(deviceName); + deviceName = null; + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/CreateDeviceTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/CreateDeviceTest.java new file mode 100644 index 0000000000..3e4ab48f5d --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/CreateDeviceTest.java @@ -0,0 +1,214 @@ +/** + * Copyright © 2016-2023 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.devicessmoke; + +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.msa.ui.base.AbstractBasePage.random; +import static org.thingsboard.server.msa.ui.utils.Const.DEVICE_PROFILE_IS_REQUIRED_MESSAGE; +import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_DEVICE_MESSAGE; +import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME; +import static org.thingsboard.server.msa.ui.utils.Const.NAME_IS_REQUIRED_MESSAGE; +import static org.thingsboard.server.msa.ui.utils.Const.SAME_NAME_WARNING_DEVICE_MESSAGE; + +@Feature("Create device") +public class CreateDeviceTest extends AbstractDeviceTest { + + @Test(groups = "smoke") + @Description("Add device after specifying the name (text/numbers /special characters)") + public void createDevice() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.addBtnC().click(); + devicePage.refreshBtn().click(); + + assertIsDisplayed(devicePage.entity(deviceName)); + } + + @Test(groups = "smoke") + @Description("Add device after specifying the name and description (text/numbers /special characters)") + public void createDeviceWithDescription() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.enterDescription(deviceName); + devicePage.addBtnC().click(); + devicePage.refreshBtn().click(); + devicePage.entity(deviceName).click(); + devicePage.setHeaderName(); + + assertThat(devicePage.getHeaderName()).as("Header of device details tab").isEqualTo(deviceName); + assertThat(devicePage.descriptionEntityView().getAttribute("value")) + .as("Description in device details tab").isEqualTo(deviceName); + } + + @Test(groups = "smoke") + @Description("Add device without the name") + public void createDeviceWithoutName() { + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.nameField().click(); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.addDeviceView()); + assertThat(devicePage.errorMessage().getText()).as("Text of warning message").isEqualTo(NAME_IS_REQUIRED_MESSAGE); + } + + @Test(groups = "smoke") + @Description("Create device only with spase in name") + public void createDeviceWithOnlySpace() { + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(" "); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.warningMessage()); + assertThat(devicePage.warningMessage().getText()).as("Text of warning message").isEqualTo(EMPTY_DEVICE_MESSAGE); + assertIsDisplayed(devicePage.addDeviceView()); + } + + @Test(groups = "smoke") + @Description("Create a device with the same name") + public void createDeviceWithSameName() { + Device device = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)); + deviceName = device.getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.warningMessage()); + assertThat(devicePage.warningMessage().getText()).as("Text of warning message").isEqualTo(SAME_NAME_WARNING_DEVICE_MESSAGE); + assertIsDisplayed(devicePage.addDeviceView()); + } + + @Test(groups = "smoke") + @Description("Add device after specifying the name (text/numbers /special characters) without refresh") + public void createDeviceWithoutRefresh() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.entity(deviceName)); + } + + @Test(groups = "smoke") + @Description("Add device without device profile") + public void createDeviceWithoutDeviceProfile() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.clearProfileFieldBtn().click(); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.errorMessage()); + assertThat(devicePage.errorMessage().getText()).as("Text of warning message").isEqualTo(DEVICE_PROFILE_IS_REQUIRED_MESSAGE); + assertIsDisplayed(devicePage.addDeviceView()); + } + + @Test(groups = "smoke") + @Description("Add device with enabled gateway") + public void createDeviceWithEnableGateway() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.checkboxGatewayCreate().click(); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.device(deviceName)); + assertIsDisplayed(devicePage.checkboxGatewayPage(deviceName)); + } + + @Test(groups = "smoke") + @Description("Add device with enabled overwrite activity time for connected") + public void createDeviceWithEnableOverwriteActivityTimeForConnected() { + deviceName = ENTITY_NAME + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.checkboxGatewayCreate().click(); + devicePage.checkboxOverwriteActivityTimeCreate().click(); + devicePage.addBtnC().click(); + devicePage.device(deviceName).click(); + + assertThat(devicePage.checkboxOverwriteActivityTimeDetails().getAttribute("class").contains("selected")) + .as("Overwrite activity time for connected is enable").isTrue(); + } + + @Test(groups = "smoke") + @Description("Add device with label") + public void createDeviceWithLabel() { + deviceName = ENTITY_NAME + random(); + String deviceLabel = "device label " + random(); + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.enterLabel(deviceLabel); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.deviceLabelOnPage(deviceName)); + assertThat(devicePage.deviceLabelOnPage(deviceName).getText()).as("Label added correctly").isEqualTo(deviceLabel); + } + + @Test(groups = "smoke") + @Description("Add device with assignee on customer") + public void createDeviceWithAssignee() { + deviceName = ENTITY_NAME + random(); + String customer = "Customer A"; + + sideBarMenuView.goToDevicesPage(); + devicePage.openCreateDeviceView(); + devicePage.enterName(deviceName); + devicePage.assignOnCustomer(customer); + devicePage.addBtnC().click(); + + assertIsDisplayed(devicePage.deviceCustomerOnPage(deviceName)); + assertThat(devicePage.deviceCustomerOnPage(deviceName).getText()) + .as("Customer added correctly").isEqualTo(customer); + } + + @Test(groups = "smoke") + @Description("Go to devices documentation page") + public void documentation() { + String urlPath = "docs/user-guide/ui/devices/"; + + sideBarMenuView.goToDevicesPage(); + devicePage.entity("Thermostat T1").click(); + devicePage.goToHelpPage(); + + assertThat(urlContains(urlPath)).as("Redirected URL contains " + urlPath).isTrue(); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteDeviceTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteDeviceTest.java new file mode 100644 index 0000000000..76028f0bc0 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteDeviceTest.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2023 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.devicessmoke; + +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME; + +@Feature("Delete device") +public class DeleteDeviceTest extends AbstractDeviceTest { + + @BeforeMethod + public void createDevice() { + Device device = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)); + deviceName = device.getName(); + } + + @Test(groups = "smoke") + @Description("Remove the device by clicking on the trash icon in the right side of device") + public void deleteDeviceByRightSideBtn() { + sideBarMenuView.goToDevicesPage(); + devicePage.deleteDeviceByRightSideBtn(deviceName); + devicePage.refreshBtn().click(); + + devicePage.assertEntityIsNotPresent(deviceName); + } + + @Test(groups = "smoke") + @Description("Remove device by mark in the checkbox and then click on the trash can icon in the menu that appears at the top") + public void deleteSelectedDevice() { + sideBarMenuView.goToDevicesPage(); + devicePage.deleteSelected(deviceName); + devicePage.refreshBtn().click(); + + devicePage.assertEntityIsNotPresent(deviceName); + } + + @Test(groups = "smoke") + @Description("Remove the device by clicking on the 'Delete device' btn in the entity view") + public void deleteDeviceFromDetailsTab() { + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.deleteDeviceFromDetailsTab(); + devicePage.refreshBtn(); + + devicePage.assertEntityIsNotPresent(deviceName); + } + + @Test(groups = "smoke") + @Description("Remove the device by clicking on the trash icon in the right side of device without refresh") + public void deleteDeviceWithoutRefresh() { + sideBarMenuView.goToDevicesPage(); + devicePage.deleteDeviceByRightSideBtn(deviceName); + + devicePage.assertEntityIsNotPresent(deviceName); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteSeveralDevicesTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteSeveralDevicesTest.java new file mode 100644 index 0000000000..48df384c63 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/DeleteSeveralDevicesTest.java @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2023 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.devicessmoke; + +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME; + +@Feature("Delete several devices") +public class DeleteSeveralDevicesTest extends AbstractDeviceTest { + + private String deviceName1; + private String deviceName2; + + @BeforeMethod + public void createDevices() { + Device device = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)); + Device device1 = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)); + deviceName1 = device.getName(); + deviceName2 = device1.getName(); + } + + @AfterMethod + public void deleteDevices() { + deleteDeviceByName(deviceName1); + deleteDeviceByName(deviceName2); + } + + @Test(groups = "smoke") + @Description("Remove several devices by mark in the checkbox and then click on the trash can icon in the menu " + + "that appears at the top") + public void deleteSeveralDevicesByTopBtn() { + sideBarMenuView.goToDevicesPage(); + devicePage.deleteSelected(2); + devicePage.refreshBtn().click(); + + devicePage.assertEntityIsNotPresent(deviceName1); + devicePage.assertEntityIsNotPresent(deviceName2); + } + + @Test(groups = "smoke") + @Description("Remove several devices by mark all the devices on the page by clicking in the topmost checkbox" + + " and then clicking on the trash icon in the menu that appears") + public void selectAllDevices() { + sideBarMenuView.goToDevicesPage(); + devicePage.selectAllCheckBox().click(); + devicePage.deleteSelectedBtn().click(); + + assertIsDisplayed(devicePage.warningPopUpTitle()); + assertThat(devicePage.warningPopUpTitle().getText()).as("Warning title contains true correct of selected devices") + .contains(String.valueOf(devicePage.markCheckbox().size())); + } + + @Test(groups = "smoke") + @Description("Remove several devices by mark in the checkbox and then click on the trash can icon in the menu " + + "that appears at the top without refresh") + public void deleteSeveralWithoutRefresh() { + sideBarMenuView.goToDevicesPage(); + devicePage.deleteSelected(2); + + devicePage.assertEntityIsNotPresent(deviceName1); + devicePage.assertEntityIsNotPresent(deviceName2); + } +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/EditDeviceTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/EditDeviceTest.java new file mode 100644 index 0000000000..77bb209485 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ui/tests/devicessmoke/EditDeviceTest.java @@ -0,0 +1,179 @@ +/** + * Copyright © 2016-2023 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.devicessmoke; + +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import org.testng.annotations.Test; +import org.thingsboard.server.msa.ui.utils.DataProviderCredential; +import org.thingsboard.server.msa.ui.utils.EntityPrototypes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.msa.ui.base.AbstractBasePage.getRandomNumber; +import static org.thingsboard.server.msa.ui.utils.Const.EMPTY_DEVICE_MESSAGE; +import static org.thingsboard.server.msa.ui.utils.Const.ENTITY_NAME; + +@Feature("Edit device") +public class EditDeviceTest extends AbstractDeviceTest { + + @Test(groups = "smoke") + @Description("Change name by edit menu") + public void changeName() { + String newDeviceName = "Changed" + getRandomNumber(); + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.setHeaderName(); + String nameBefore = devicePage.getHeaderName(); + devicePage.editPencilBtn().click(); + devicePage.changeNameEditMenu(newDeviceName); + devicePage.doneBtnEditView().click(); + deviceName = newDeviceName; + devicePage.setHeaderName(); + String nameAfter = devicePage.getHeaderName(); + + assertThat(nameAfter).as("The name has changed").isNotEqualTo(nameBefore); + assertThat(nameAfter).as("The name has changed correctly").isEqualTo(newDeviceName); + } + + @Test(groups = "smoke") + @Description("Delete name and save") + public void deleteName() { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.changeNameEditMenu(""); + + assertIsDisable(devicePage.doneBtnEditViewVisible()); + } + + @Test(groups = "smoke") + @Description("Save only with space") + public void saveOnlyWithSpace() { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.changeNameEditMenu(" "); + devicePage.doneBtnEditView().click(); + + assertIsDisplayed(devicePage.warningMessage()); + assertThat(devicePage.warningMessage().getText()).as("Text of warning message").isEqualTo(EMPTY_DEVICE_MESSAGE); + } + + @Test(groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editMenuDescription") + @Description("Write the description and save the changes/Change the description and save the changes/Delete the description and save the changes") + public void editDescription(String description, String newDescription, String finalDescription) { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME, description)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.descriptionEntityView().sendKeys(newDescription); + devicePage.doneBtnEditView().click(); + devicePage.setDescription(); + + assertThat(devicePage.getDescription()).as("The description changed correctly").isEqualTo(finalDescription); + } + + @Test(groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "enable") + @Description("Enable gateway mode/Disable gateway") + public void isGateway(boolean isGateway) { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME, isGateway)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.checkboxGatewayEdit().click(); + devicePage.doneBtnEditView().click(); + + if (isGateway) { + assertThat(devicePage.checkboxGatewayDetailsTab().getAttribute("class").contains("selected")) + .as("Gateway is disable").isFalse(); + } else { + assertThat(devicePage.checkboxGatewayDetailsTab().getAttribute("class").contains("selected")) + .as("Gateway is enable").isTrue(); + } + } + + @Test(groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "enable") + @Description("Enable overwrite activity time for connected/Disable overwrite activity time for connected") + public void isOverwriteActivityTimeForConnectedDevice(boolean isOverwriteActivityTimeForConnected) { + deviceName = testRestClient.postDevice("", + EntityPrototypes.defaultDevicePrototype(ENTITY_NAME, true, isOverwriteActivityTimeForConnected)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.checkboxOverwriteActivityTimeEdit().click(); + devicePage.doneBtnEditView().click(); + + if (isOverwriteActivityTimeForConnected) { + assertThat(devicePage.checkboxOverwriteActivityTimeDetails().getAttribute("class").contains("selected")) + .as("Overwrite activity time for connected is disable").isFalse(); + } else { + assertThat(devicePage.checkboxOverwriteActivityTimeDetails().getAttribute("class").contains("selected")) + .as("Overwrite activity time for connected is enable").isTrue(); + } + } + + @Test(groups = "smoke") + @Description("Change device profile") + public void changeDeviceProfile() { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.changeDeviceProfile("DEFAULT"); + devicePage.doneBtnEditView().click(); + + assertIsDisplayed(devicePage.deviceProfileRedirectedBtn()); + assertThat(devicePage.deviceProfileRedirectedBtn().getText()).as("Profile changed correctly").isEqualTo("DEFAULT"); + } + + @Test(groups = "smoke") + @Description("Save without device profile") + public void saveWithoutDeviceProfile() { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.clearProfileFieldBtn().click(); + + assertIsDisable(devicePage.doneBtnEditViewVisible()); + } + + @Test(groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "editDeviceLabel") + @Description("Write the label and save the changes/Change the label and save the changes/Delete the label and save the changes") + public void editLabel(String label, String newLabel, String finalLabel) { + deviceName = testRestClient.postDevice("", EntityPrototypes.defaultDevicePrototype(ENTITY_NAME, "", label)).getName(); + + sideBarMenuView.goToDevicesPage(); + devicePage.entity(deviceName).click(); + devicePage.editPencilBtn().click(); + devicePage.deviceLabelEditField().sendKeys(newLabel); + devicePage.doneBtnEditView().click(); + devicePage.setLabel(); + + assertThat(devicePage.getLabel()).as("The label changed correctly").isEqualTo(finalLabel); + } +} 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 index eaeaa6e61f..5882556ff8 100644 --- 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 @@ -99,7 +99,7 @@ public class RuleChainEditMenuTest extends AbstractRuleChainTest { assertThat(ruleChainsPage.getDescription()).as("The description changed correctly").isEqualTo(finalDescription); } - @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "debugMode") + @Test(priority = 20, groups = "smoke", dataProviderClass = DataProviderCredential.class, dataProvider = "enable") @Description("Enable debug mode/Disable debug mode") public void debugMode(boolean debugMode) { ruleChainName = ENTITY_NAME + random(); 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 index 7b64eaa1b4..28bdac556f 100644 --- 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 @@ -36,10 +36,13 @@ public class Const { public static final String EMPTY_CUSTOMER_MESSAGE = "Customer title should be specified!"; public static final String EMPTY_DEVICE_PROFILE_MESSAGE = "Device profile name should be specified!"; public static final String EMPTY_ASSET_PROFILE_MESSAGE = "Asset profile name should be specified!"; + public static final String EMPTY_DEVICE_MESSAGE = "Device name 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 SAME_NAME_WARNING_DEVICE_PROFILE_MESSAGE = "Device profile with such name already exists!"; public static final String SAME_NAME_WARNING_ASSET_PROFILE_MESSAGE = "Asset profile with such name already exists!"; + public static final String SAME_NAME_WARNING_DEVICE_MESSAGE = "Device with such name already exists!"; public static final String PHONE_NUMBER_ERROR_MESSAGE = "Phone number is invalid or not possible"; public static final String NAME_IS_REQUIRED_MESSAGE = "Name is required."; -} \ No newline at end of file + public static final String DEVICE_PROFILE_IS_REQUIRED_MESSAGE = "Device profile is required"; +} 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 index 6e56abff2e..e7ee242cab 100644 --- 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 @@ -151,9 +151,19 @@ public class DataProviderCredential { } @DataProvider - public static Object[][] debugMode() { + public static Object[][] enable() { return new Object[][]{ {false}, {true}}; } + + @DataProvider + public static Object[][] editDeviceLabel() { + String newLabel = "Label" + getRandomNumber(); + String label = "Label"; + return new Object[][]{ + {"", newLabel, newLabel}, + {label, newLabel, label + newLabel}, + {label, Keys.CONTROL + "A" + Keys.BACK_SPACE, ""}}; + } } 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 index 6f2d9560f4..ec66442bd2 100644 --- 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 @@ -192,6 +192,41 @@ public class EntityPrototypes { return device; } + public static Device defaultDevicePrototype(String name, String description) { + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setType("DEFAULT"); + device.setAdditionalInfo(JacksonUtil.newObjectNode().put("description", description)); + return device; + } + + public static Device defaultDevicePrototype(String name, String description, String label) { + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setType("DEFAULT"); + device.setAdditionalInfo(JacksonUtil.newObjectNode().put("description", description)); + device.setLabel(label); + return device; + } + + public static Device defaultDevicePrototype(String name, boolean gateway) { + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setType("DEFAULT"); + device.setAdditionalInfo(JacksonUtil.newObjectNode().put("gateway", gateway)); + return device; + } + + public static Device defaultDevicePrototype(String name, boolean gateway, boolean overwriteActivityTime) { + Device device = new Device(); + device.setName(name + RandomStringUtils.randomAlphanumeric(7)); + device.setType("DEFAULT"); + device.setAdditionalInfo(JacksonUtil.newObjectNode() + .put("gateway", gateway) + .put("overwriteActivityTime", overwriteActivityTime)); + return device; + } + public static Asset defaultAssetPrototype(String name, CustomerId id) { Asset asset = new Asset(); asset.setName(name + RandomStringUtils.randomAlphanumeric(7)); diff --git a/msa/black-box-tests/src/test/resources/all.xml b/msa/black-box-tests/src/test/resources/all.xml index 155aac38e1..95248a6fe6 100644 --- a/msa/black-box-tests/src/test/resources/all.xml +++ b/msa/black-box-tests/src/test/resources/all.xml @@ -67,4 +67,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/msa/black-box-tests/src/test/resources/smokeDevices.xml b/msa/black-box-tests/src/test/resources/smokeDevices.xml new file mode 100644 index 0000000000..c255f9ed53 --- /dev/null +++ b/msa/black-box-tests/src/test/resources/smokeDevices.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 index 9dbf8dfc0c..9de384d15c 100644 --- a/msa/black-box-tests/src/test/resources/uiTests.xml +++ b/msa/black-box-tests/src/test/resources/uiTests.xml @@ -62,4 +62,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 896825ffd0..2f23aec399 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -47,7 +47,7 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends wget nmap procps gnupg2 \ && echo "deb http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee --append /etc/apt/sources.list.d/pgdg.list > /dev/null \ && wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O- | apt-key add - \ - && echo 'deb http://downloads.apache.org/cassandra/debian 40x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null \ + && echo "deb https://debian.cassandra.apache.org 40x main" | tee -a /etc/apt/sources.list.d/cassandra.sources.list > /dev/null \ && wget -q https://downloads.apache.org/cassandra/KEYS -O- | apt-key add - \ && apt-get update \ && apt-get install -y --no-install-recommends cassandra cassandra-tools postgresql-${PG_MAJOR} \ diff --git a/security.md b/security.md new file mode 100644 index 0000000000..9f917e5f00 --- /dev/null +++ b/security.md @@ -0,0 +1,17 @@ +# Security Policy + +## Reporting a Vulnerability + +Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to Thingsboard privately, +to minimize attacks against current users of Thingsboard before they are fixed. Vulnerabilities will be investigated and release as soon as possible. + +To report a vulnerability or a security-related issue, please email the private address security@thingsboard.io with the details of the vulnerability. +Emails will be addressed within 3 business days, including a detailed plan to investigate the issue and any potential workarounds to perform in the meantime. +Do not report non-security-impacting bugs through this channel. Use GitHub issues instead. + +**Proposed Email Content** +Provide a descriptive subject line and in the body of the email include the following information: + +- Basic identity information, such as your name and your affiliation or company. +- Detailed steps to reproduce the vulnerability (log errors, screenshots are all helpful to us). +- Description of the effects of the vulnerability on Thingsboard. \ No newline at end of file diff --git a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js index c309eb38aa..bc22e806c3 100644 --- a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js +++ b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js @@ -5026,6 +5026,21 @@ var JSHINT = (function() { return that; }, 20); } + function nullSafeProperty(s) { + console.log("test " + s); + symbol(s, 20).exps = true; + return infix(s, function(context, left, that) { + if (state.option.bitwise) { + warning("W016", that, that.id); + } + + checkLeftSideAssign(context, left, that); + + that.right = expression(context, 10); + + return that; + }, 20); + } function suffix(s) { var x = symbol(s, 150); @@ -5539,6 +5554,9 @@ var JSHINT = (function() { bitwiseassignop("<<="); bitwiseassignop(">>="); bitwiseassignop(">>>="); + + nullSafeProperty(".?"); + infix(",", function(context, left, that) { if (state.option.nocomma) { warning("W127", that); @@ -9450,6 +9468,12 @@ Lexer.prototype = { switch (ch1) { case ".": + if (ch1 === "." && this.peek(1) === "?") { + return { + type: Token.Punctuator, + value: ".?" + }; + } if ((/^[0-9]$/).test(this.peek(1))) { return null; }